l@270: /** @mainpage OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library 
l@270: 
l@270:   Before using this file please take the time to read the OSC spec, it
l@270:   is short and not complicated: http://opensoundcontrol.org/spec-1_0
l@270: 
l@270:   Features: 
l@270:     - handles basic OSC types: TFihfdsb
l@270:     - handles bundles
l@270:     - handles OSC pattern-matching rules (wildcards etc in message paths)
l@270:     - portable on win / macos / linux
l@270:     - robust wrt malformed packets
l@270:     - optional udp transport for packets
l@270:     - concise, all in a single .h file
l@270:     - does not throw exceptions
l@270: 
l@270:   does not:
l@270:     - take into account timestamp values.
l@270:     - provide a cpu-scalable message dispatching.
l@270:     - not suitable for use inside a realtime thread as it allocates memory when 
l@270:     building or reading messages.
l@270: 
l@270: 
l@270:   There are basically 3 classes of interest:
l@270:     - oscpkt::Message       : read/write the content of an OSC message
l@270:     - oscpkt::PacketReader  : read the bundles/messages embedded in an OSC packet
l@270:     - oscpkt::PacketWriter  : write bundles/messages into an OSC packet
l@270: 
l@270:   And optionaly:
l@270:     - oscpkt::UdpSocket     : read/write OSC packets over UDP.
l@270: 
l@270:   @example: oscpkt_demo.cc
l@270:   @example: oscpkt_test.cc
l@270: */
l@270: 
l@270: /* Copyright (C) 2010  Julien Pommier
l@270: 
l@270:   This software is provided 'as-is', without any express or implied
l@270:   warranty.  In no event will the authors be held liable for any damages
l@270:   arising from the use of this software.
l@270: 
l@270:   Permission is granted to anyone to use this software for any purpose,
l@270:   including commercial applications, and to alter it and redistribute it
l@270:   freely, subject to the following restrictions:
l@270: 
l@270:   1. The origin of this software must not be misrepresented; you must not
l@270:      claim that you wrote the original software. If you use this software
l@270:      in a product, an acknowledgment in the product documentation would be
l@270:      appreciated but is not required.
l@270:   2. Altered source versions must be plainly marked as such, and must not be
l@270:      misrepresented as being the original software.
l@270:   3. This notice may not be removed or altered from any source distribution.
l@270: 
l@270:   (this is the zlib license)
l@270: */
l@270: 
l@270: #ifndef OSCPKT_HH
l@270: #define OSCPKT_HH
l@270: 
l@270: #ifndef _MSC_VER
l@270: #include <stdint.h>
l@270: #else
l@270: namespace oscpkt {
l@270:   typedef __int32 int32_t;
l@270:   typedef unsigned __int32 uint32_t;
l@270:   typedef __int64 int64_t;
l@270:   typedef unsigned __int64 uint64_t;
l@270: }
l@270: #endif
l@270: #include <cstring>
l@270: #include <cassert>
l@270: #include <string>
l@270: #include <vector>
l@270: #include <list>
l@270: 
l@270: #if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST)
l@270: #include <iostream>
l@270: #endif
l@270: 
l@270: namespace oscpkt {
l@270: 
l@270: /**
l@270:    OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second.
l@270: */
l@270: class TimeTag {
l@270:   uint64_t v;
l@270: public:
l@270:   TimeTag() : v(1) {}
l@270:   explicit TimeTag(uint64_t w): v(w) {}
l@270:   operator uint64_t() const { return v; }
l@270:   static TimeTag immediate() { return TimeTag(1); }
l@270: };
l@270: 
l@270: /* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */
l@270: enum {
l@270:   TYPE_TAG_TRUE = 'T',
l@270:   TYPE_TAG_FALSE = 'F',
l@270:   TYPE_TAG_INT32 = 'i',
l@270:   TYPE_TAG_INT64 = 'h',
l@270:   TYPE_TAG_FLOAT = 'f',
l@270:   TYPE_TAG_DOUBLE = 'd',
l@270:   TYPE_TAG_STRING = 's',
l@270:   TYPE_TAG_BLOB = 'b'
l@270: };
l@270: 
l@270: /* a few utility functions follow.. */
l@270:   
l@270: // round to the next multiple of 4, works for size_t and pointer arguments
l@270: template <typename Type> Type ceil4(Type p) { return (Type)((size_t(p) + 3)&(~size_t(3))); }
l@270: 
l@270: // check that a memory area is zero padded until the next address which is a multiple of 4
l@270: inline bool isZeroPaddingCorrect(const char *p) {
l@270:   const char *q = ceil4(p);
l@270:   for (;p < q; ++p)
l@270:     if (*p != 0) { return false; }
l@270:   return true;
l@270: }
l@270: 
l@270: // stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes.
l@270: template <typename POD> union PodBytes {
l@270:   char bytes[sizeof(POD)];
l@270:   POD  value;
l@270: };
l@270: 
l@270: inline bool isBigEndian() { // a compile-time constant would certainly improve performances..
l@270:   PodBytes<int32_t> p; p.value = 0x12345678;
l@270:   return p.bytes[0] == 0x12;
l@270: }
l@270: 
l@270: /** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */
l@270: template <typename POD> POD bytes2pod(const char *bytes) {
l@270:   PodBytes<POD> p; 
l@270:   for (size_t i=0; i < sizeof(POD); ++i) {
l@270:     if (isBigEndian()) 
l@270:       p.bytes[i] = bytes[i];
l@270:     else
l@270:       p.bytes[i] = bytes[sizeof(POD) - i - 1];
l@270:   }
l@270:   return p.value;
l@270: }
l@270: 
l@270: /** stored a POD type into an unaligned bytes array, using little endian representation */
l@270: template <typename POD> void pod2bytes(const POD value, char *bytes) {
l@270:   PodBytes<POD> p; p.value = value; 
l@270:   for (size_t i=0; i < sizeof(POD); ++i) {
l@270:     if (isBigEndian()) 
l@270:       bytes[i] = p.bytes[i];
l@270:     else
l@270:       bytes[i] = p.bytes[sizeof(POD) - i - 1];
l@270:   }
l@270: }
l@270: 
l@270: /** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */
l@270: struct Storage {
l@270:   std::vector<char> data;
l@270:   Storage() { data.reserve(200); }
l@270:   char *getBytes(size_t sz) {
l@270:     assert((data.size() & 3) == 0);
l@270:     if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz)*2); }
l@270:     size_t sz4 = ceil4(sz);
l@270:     size_t pos = data.size(); 
l@270:     data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK
l@270:     return &(data[pos]);
l@270:   }
l@270:   char *begin() { return data.size() ? &data.front() : 0; }
l@270:   char *end() { return begin() + size(); }
l@270:   const char *begin() const { return data.size() ? &data.front() : 0; }
l@270:   const char *end() const { return begin() + size(); }
l@270:   size_t size() const { return data.size(); }
l@270:   void assign(const char *beg, const char *end) { data.assign(beg, end); }
l@270:   void clear() { data.resize(0); }
l@270: };
l@270: 
l@270: /** check if the path matches the supplied path pattern , according to the OSC spec pattern 
l@270:     rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */
l@270: bool fullPatternMatch(const std::string &pattern, const std::string &path);
l@270: /** check if the path matches the beginning of pattern */
l@270: bool partialPatternMatch(const std::string &pattern, const std::string &path);
l@270: 
l@270: #if defined(OSCPKT_DEBUG)
l@270: #define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0)
l@270: #else
l@270: #define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0)
l@270: #endif
l@270: 
l@270: typedef enum { OK_NO_ERROR=0, 
l@270:                // errors raised by the Message class:
l@270:                MALFORMED_ADDRESS_PATTERN, MALFORMED_TYPE_TAGS, MALFORMED_ARGUMENTS, UNHANDLED_TYPE_TAGS, 
l@270:                // errors raised by ArgReader
l@270:                TYPE_MISMATCH, NOT_ENOUGH_ARG, PATTERN_MISMATCH, 
l@270:                // errors raised by PacketReader/PacketWriter
l@270:                INVALID_BUNDLE, INVALID_PACKET_SIZE, BUNDLE_REQUIRED_FOR_MULTI_MESSAGES } ErrorCode;
l@270: 
l@270: /**
l@270:    struct used to hold an OSC message that will be written or read.
l@270: 
l@270:    The list of arguments is exposed as a sort of queue. You "pop"
l@270:    arguments from the front of the queue when reading, you push
l@270:    arguments at the back of the queue when writing.
l@270: 
l@270:    Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")...
l@270: 
l@270:    Example of use:
l@270: 
l@270:    creation of a message:
l@270:    @code
l@270:    msg.init("/foo").pushInt32(4).pushStr("bar");
l@270:    @endcode
l@270:    reading a message, with error detection:
l@270:    @code
l@270:    if (msg.match("/foo/b*ar/plop")) {
l@270:      int i; std::string s; std::vector<char> b;
l@270:      if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) {
l@270:        process message...;
l@270:      } else arguments mismatch;
l@270:    }
l@270:    @endcode
l@270: */
l@270: class Message {
l@270:   TimeTag time_tag;
l@270:   std::string address;
l@270:   std::string type_tags;
l@270:   std::vector<std::pair<size_t, size_t> > arguments; // array of pairs (pos,size), pos being an index into the 'storage' array.
l@270:   Storage storage; // the arguments data is stored here
l@270:   ErrorCode err;
l@270: public:  
l@270:   /** ArgReader is used for popping arguments from a Message, holds a
l@270:       pointer to the original Message, and maintains a local error code */
l@270:   class ArgReader {
l@270:     const Message *msg;
l@270:     ErrorCode err;
l@270:     size_t arg_idx; // arg index of the next arg that will be popped out.
l@270:   public:
l@270:     ArgReader(const Message &m, ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0) { 
l@270:       if (e != OK_NO_ERROR && err == OK_NO_ERROR) err=e; 
l@270:     }
l@270:     ArgReader(const ArgReader &other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {}
l@270:     bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; }
l@270:     bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; }
l@270:     bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; }
l@270:     bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; }
l@270:     bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; }
l@270:     bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; }
l@270:     bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; }
l@270: 
l@270:     size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; }
l@270:     bool isOk() const { return err == OK_NO_ERROR; }
l@270:     operator bool() const { return isOk(); } // implicit bool conversion is handy here
l@270:     /** call this at the end of the popXXX() chain to make sure everything is ok and 
l@270:         all arguments have been popped */
l@270:     bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; }
l@270:     ErrorCode getErr() const { return err; }
l@270: 
l@270:     /** retrieve an int32 argument */
l@270:     ArgReader &popInt32(int32_t &i) { return popPod<int32_t>(TYPE_TAG_INT32, i); }
l@270:     /** retrieve an int64 argument */
l@270:     ArgReader &popInt64(int64_t &i) { return popPod<int64_t>(TYPE_TAG_INT64, i); }
l@270:     /** retrieve a single precision floating point argument */
l@270:     ArgReader &popFloat(float &f) { return popPod<float>(TYPE_TAG_FLOAT, f); }
l@270:     /** retrieve a double precision floating point argument */
l@270:     ArgReader &popDouble(double &d) { return popPod<double>(TYPE_TAG_DOUBLE, d); }
l@270:     /** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */
l@270:     ArgReader &popStr(std::string &s) {
l@270:       if (precheck(TYPE_TAG_STRING)) {
l@270:         s = argBeg(arg_idx++);
l@270:       }
l@270:       return *this;
l@270:     }
l@270:     /** retrieve a binary blob */
l@270:     ArgReader &popBlob(std::vector<char> &b) { 
l@270:       if (precheck(TYPE_TAG_BLOB)) {
l@270:         b.assign(argBeg(arg_idx)+4, argEnd(arg_idx)); 
l@270:         ++arg_idx;
l@270:       }
l@270:       return *this;
l@270:     }
l@270:     /** retrieve a boolean argument */
l@270:     ArgReader &popBool(bool &b) {
l@270:       b = false;
l@270:       if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
l@270:       else if (currentTypeTag() == TYPE_TAG_TRUE) b = true;
l@270:       else if (currentTypeTag() == TYPE_TAG_FALSE) b = false;
l@270:       else OSCPKT_SET_ERR(TYPE_MISMATCH);
l@270:       ++arg_idx;
l@270:       return *this;
l@270:     }
l@270:     /** skip whatever comes next */
l@270:     ArgReader &pop() {
l@270:       if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
l@270:       else ++arg_idx;
l@270:       return *this;
l@270:     }
l@270:   private:
l@270:     const char *argBeg(size_t idx) {
l@270:       if (err || idx >= msg->arguments.size()) return 0; 
l@270:       else return msg->storage.begin() + msg->arguments[idx].first;
l@270:     }
l@270:     const char *argEnd(size_t idx) {
l@270:       if (err || idx >= msg->arguments.size()) return 0; 
l@270:       else return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second;
l@270:     }
l@270:     int currentTypeTag() {
l@270:       if (!err && arg_idx < msg->type_tags.size()) return msg->type_tags[arg_idx];
l@270:       else OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
l@270:       return -1;
l@270:     }
l@270:     template <typename POD> ArgReader &popPod(int tag, POD &v) {
l@270:       if (precheck(tag)) {
l@270:         v = bytes2pod<POD>(argBeg(arg_idx));
l@270:         ++arg_idx;
l@270:       } else v = POD(0);
l@270:       return *this;
l@270:     }
l@270:     /* pre-check stuff before popping an argument from the message */
l@270:     bool precheck(int tag) { 
l@270:       if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
l@270:       else if (!err && currentTypeTag() != tag) OSCPKT_SET_ERR(TYPE_MISMATCH);
l@270:       return err == OK_NO_ERROR;
l@270:     }
l@270:   };
l@270: 
l@270:   Message() { clear(); }
l@270:   Message(const std::string &s, TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {}
l@270:   Message(const void *ptr, size_t sz, TimeTag tt = TimeTag::immediate()) { buildFromRawData(ptr, sz); time_tag = tt; }
l@270: 
l@270:   bool isOk() const { return err == OK_NO_ERROR; }
l@270:   ErrorCode getErr() const { return err; }
l@270: 
l@270:   /** return the type_tags string, with its initial ',' stripped. */
l@270:   const std::string &typeTags() const { return type_tags; }
l@270:   /** retrieve the address pattern. If you want to follow to the whole OSC spec, you
l@270:       have to handle its matching rules for address specifications -- this file does 
l@270:       not provide this functionality */
l@270:   const std::string &addressPattern() const { return address; }
l@270:   TimeTag timeTag() const { return time_tag; }
l@270:   /** clear the message and start a new message with the supplied address and time_tag. */
l@270:   Message &init(const std::string &addr, TimeTag tt = TimeTag::immediate()) {
l@270:     clear();
l@270:     address = addr; time_tag = tt;
l@270:     if (address.empty() || address[0] != '/') OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN);     
l@270:     return *this;
l@270:   }
l@270: 
l@270:   /** start a matching test. The typical use-case is to follow this by
l@270:       a sequence of calls to popXXX() and a final call to
l@270:       isOkNoMoreArgs() which will allow to check that everything went
l@270:       fine. For example:
l@270:       @code
l@270:       if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); } 
l@270:       else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); }
l@270:       else cerr << "unhandled message: " << msg << "\n";
l@270:       @endcode
l@270:   */
l@270:   ArgReader match(const std::string &test) const {
l@270:     return ArgReader(*this, fullPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH);
l@270:   }
l@270:   /** return true if the 'test' path matched by the first characters of addressPattern().
l@270:       For ex. ("/foo/bar").partialMatch("/foo/") is true */
l@270:   ArgReader partialMatch(const std::string &test) const {
l@270:     return ArgReader(*this, partialPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH);
l@270:   }
l@270:   ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); }
l@270: 
l@270:   /** build the osc message for raw data (the message will keep a copy of that data) */
l@270:   void buildFromRawData(const void *ptr, size_t sz) {
l@270:     clear();
l@270:     storage.assign((const char*)ptr, (const char*)ptr + sz);
l@270:     const char *address_beg = storage.begin();
l@270:     const char *address_end = (const char*)memchr(address_beg, 0, storage.end()-address_beg);
l@270:     if (!address_end || !isZeroPaddingCorrect(address_end+1) || address_beg[0] != '/') { 
l@270:       OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); return; 
l@270:     } else address.assign(address_beg, address_end);
l@270: 
l@270:     const char *type_tags_beg = ceil4(address_end+1);
l@270:     const char *type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end()-type_tags_beg);
l@270:     if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end+1) || type_tags_beg[0] != ',') { 
l@270:       OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS); return; 
l@270:     } else type_tags.assign(type_tags_beg+1, type_tags_end); // we do not copy the initial ','
l@270: 
l@270:     const char *arg = ceil4(type_tags_end+1); assert(arg <= storage.end()); 
l@270:     size_t iarg = 0;
l@270:     while (isOk() && iarg < type_tags.size()) {
l@270:       assert(arg <= storage.end()); 
l@270:       size_t len = getArgSize(type_tags[iarg], arg);
l@270:       if (isOk()) arguments.push_back(std::make_pair(arg - storage.begin(), len));
l@270:       arg += ceil4(len); ++iarg;
l@270:     }
l@270:     if (iarg < type_tags.size() || arg != storage.end()) {
l@270:       OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
l@270:     }
l@270:   }
l@270: 
l@270:   /* below are all the functions that serve when *writing* a message */
l@270:   Message &pushBool(bool b) { 
l@270:     type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE); 
l@270:     arguments.push_back(std::make_pair(storage.size(), storage.size()));
l@270:     return *this;
l@270:   }
l@270:   Message &pushInt32(int32_t i) { return pushPod(TYPE_TAG_INT32, i); }
l@270:   Message &pushInt64(int64_t h) { return pushPod(TYPE_TAG_INT64, h); }
l@270:   Message &pushFloat(float f) { return pushPod(TYPE_TAG_FLOAT, f); }
l@270:   Message &pushDouble(double d) { return pushPod(TYPE_TAG_DOUBLE, d); }
l@270:   Message &pushStr(const std::string &s) {
l@270:     assert(s.size() < 2147483647); // insane values are not welcome
l@270:     type_tags += TYPE_TAG_STRING;
l@270:     arguments.push_back(std::make_pair(storage.size(), s.size() + 1));
l@270:     strcpy(storage.getBytes(s.size()+1), s.c_str());
l@270:     return *this;
l@270:   }
l@270:   Message &pushBlob(void *ptr, size_t num_bytes) {
l@270:     assert(num_bytes < 2147483647); // insane values are not welcome
l@270:     type_tags += TYPE_TAG_BLOB; 
l@270:     arguments.push_back(std::make_pair(storage.size(), num_bytes+4));
l@270:     pod2bytes<int32_t>((int32_t)num_bytes, storage.getBytes(4));
l@270:     if (num_bytes)
l@270:       memcpy(storage.getBytes(num_bytes), ptr, num_bytes);
l@270:     return *this;
l@270:   }
l@270: 
l@270:   /** reset the message to a clean state */
l@270:   void clear() { 
l@270:     address.clear(); type_tags.clear(); storage.clear(); arguments.clear(); 
l@270:     err = OK_NO_ERROR; time_tag = TimeTag::immediate();
l@270:   }
l@270: 
l@270:   /** write the raw message data (used by PacketWriter) */
l@270:   void packMessage(Storage &s, bool write_size) const {
l@270:     if (!isOk()) return;
l@270:     size_t l_addr = address.size()+1, l_type = type_tags.size()+2;
l@270:     if (write_size) 
l@270:       pod2bytes<uint32_t>(uint32_t(ceil4(l_addr) + ceil4(l_type) + ceil4(storage.size())), s.getBytes(4));
l@270:     strcpy(s.getBytes(l_addr), address.c_str());
l@270:     strcpy(s.getBytes(l_type), ("," + type_tags).c_str());
l@270:     if (storage.size())
l@270:       memcpy(s.getBytes(storage.size()), const_cast<Storage&>(storage).begin(), storage.size());
l@270:   }
l@270: 
l@270: private:
l@270: 
l@270:   /* get the number of bytes occupied by the argument */
l@270:   size_t getArgSize(int type, const char *p) {
l@270:     if (err) return 0;
l@270:     size_t sz = 0;
l@270:     assert(p >= storage.begin() && p <= storage.end());
l@270:     switch (type) {
l@270:       case TYPE_TAG_TRUE:
l@270:       case TYPE_TAG_FALSE: sz = 0; break;
l@270:       case TYPE_TAG_INT32: 
l@270:       case TYPE_TAG_FLOAT: sz = 4; break;
l@270:       case TYPE_TAG_INT64: 
l@270:       case TYPE_TAG_DOUBLE: sz = 8; break;
l@270:       case TYPE_TAG_STRING: {
l@270:         const char *q = (const char*)memchr(p, 0, storage.end()-p);
l@270:         if (!q) OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
l@270:         else sz = (q-p)+1;
l@270:       } break;
l@270:       case TYPE_TAG_BLOB: {
l@270:         if (p == storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
l@270:         sz = 4+bytes2pod<uint32_t>(p);
l@270:       } break;
l@270:       default: {
l@270:         OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS); return 0;
l@270:       } break;
l@270:     }
l@270:     if (p+sz > storage.end() || /* string or blob too large.. */
l@270:         p+sz < p /* or even blob so large that it did overflow */) { 
l@270:       OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; 
l@270:     }
l@270:     if (!isZeroPaddingCorrect(p+sz)) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
l@270:     return sz;
l@270:   }
l@270: 
l@270:   template <typename POD> Message &pushPod(int tag, POD v) {
l@270:     type_tags += (char)tag; 
l@270:     arguments.push_back(std::make_pair(storage.size(), sizeof(POD)));
l@270:     pod2bytes(v, storage.getBytes(sizeof(POD))); 
l@270:     return *this;
l@270:   }
l@270: 
l@270: #ifdef OSCPKT_OSTREAM_OUTPUT
l@270:   friend std::ostream &operator<<(std::ostream &os, const Message &msg) {
l@270:     os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=[";
l@270:     Message::ArgReader arg(msg);
l@270:     while (arg.nbArgRemaining() && arg.isOk()) {
l@270:       if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); }
l@270:       else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; }
l@270:       else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; }
l@270:       else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; }
l@270:       else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; }
l@270:       else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; }
l@270:       else if (arg.isBlob()) { std::vector<char> b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; }
l@270:       else {
l@270:         assert(0); // I forgot a case..
l@270:       }
l@270:       if (arg.nbArgRemaining()) os << ", ";
l@270:     }
l@270:     if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); }
l@270:     os << "]";
l@270:     return os;
l@270:   }
l@270: #endif
l@270: };
l@270: 
l@270: /**
l@270:    parse an OSC packet and extracts the embedded OSC messages. 
l@270: */
l@270: class PacketReader {
l@270: public:
l@270:   PacketReader() { err = OK_NO_ERROR; }
l@270:   /** pointer and size of the osc packet to be parsed. */
l@270:   PacketReader(const void *ptr, size_t sz) { init(ptr, sz); }
l@270: 
l@270:   void init(const void *ptr, size_t sz) {
l@270:     err = OK_NO_ERROR; messages.clear();
l@270:     if ((sz%4) == 0) { 
l@270:       parse((const char*)ptr, (const char *)ptr+sz, TimeTag::immediate());
l@270:     } else OSCPKT_SET_ERR(INVALID_PACKET_SIZE);
l@270:     it_messages = messages.begin();
l@270:   }
l@270:   
l@270:   /** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */
l@270:   Message *popMessage() {
l@270:     if (!err && !messages.empty() && it_messages != messages.end()) return &*it_messages++;
l@270:     else return 0;
l@270:   }
l@270:   bool isOk() const { return err == OK_NO_ERROR; }
l@270:   ErrorCode getErr() const { return err; }
l@270: 
l@270: private:
l@270:   std::list<Message> messages;
l@270:   std::list<Message>::iterator it_messages;
l@270:   ErrorCode err;
l@270:   
l@270:   void parse(const char *beg, const char *end, TimeTag time_tag) {
l@270:     assert(beg <= end && !err); assert(((end-beg)%4)==0);
l@270:     
l@270:     if (beg == end) return;
l@270:     if (*beg == '#') {
l@270:       /* it's a bundle */
l@270:       if (end - beg >= 20 
l@270:           && memcmp(beg, "#bundle\0", 8) == 0) {
l@270:         TimeTag time_tag2(bytes2pod<uint64_t>(beg+8));
l@270:         const char *pos = beg + 16;
l@270:         do {
l@270:           uint32_t sz = bytes2pod<uint32_t>(pos); pos += 4;
l@270:           if ((sz&3) != 0 || pos + sz > end || pos+sz < pos) {
l@270:             OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270:           } else {
l@270:             parse(pos, pos+sz, time_tag2);
l@270:             pos += sz;
l@270:           }
l@270:         } while (!err && pos != end);
l@270:       } else {
l@270:         OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270:       }
l@270:     } else {
l@270:       messages.push_back(Message(beg, end-beg, time_tag));
l@270:       if (!messages.back().isOk()) OSCPKT_SET_ERR(messages.back().getErr());
l@270:     }
l@270:   }
l@270: };
l@270: 
l@270: 
l@270: /**
l@270:    Assemble messages into an OSC packet. Example of use:
l@270:    @code
l@270:    PacketWriter pkt; 
l@270:    Message msg;
l@270:    pkt.startBundle(); 
l@270:    pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f));
l@270:    pkt.addMessage(msg.init("/bar").pushBool(false));
l@270:    pkt.endBundle();
l@270:    if (pkt.isOk()) {
l@270:      send(pkt.data(), pkt.size());
l@270:    }
l@270:    @endcode
l@270: */
l@270: class PacketWriter {
l@270: public:
l@270:   PacketWriter() { init(); }
l@270:   PacketWriter &init() { err = OK_NO_ERROR; storage.clear(); bundles.clear(); return *this; }
l@270:   
l@270:   /** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to 
l@270:       put them in a bundle. Nested bundles inside bundles are also allowed. */
l@270:   PacketWriter &startBundle(TimeTag ts = TimeTag::immediate()) {
l@270:     char *p;
l@270:     if (bundles.size()) storage.getBytes(4); // hold the bundle size
l@270:     p = storage.getBytes(8); strcpy(p, "#bundle"); bundles.push_back(p - storage.begin());
l@270:     p = storage.getBytes(8); pod2bytes<uint64_t>(ts, p);
l@270:     return *this;
l@270:   }
l@270:   /** close the current bundle. */
l@270:   PacketWriter &endBundle() {
l@270:     if (bundles.size()) {
l@270:       if (storage.size() - bundles.back() == 16) {
l@270:         pod2bytes<uint32_t>(0, storage.getBytes(4)); // the 'empty bundle' case, not very elegant
l@270:       }
l@270:       if (bundles.size()>1) { // no size stored for the top-level bundle
l@270:         pod2bytes<uint32_t>(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back()-4);
l@270:       }
l@270:       bundles.pop_back();      
l@270:     } else OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270:     return *this;
l@270:   }
l@270: 
l@270:   /** insert an Osc message into the current bundle / packet.
l@270:    */
l@270:   PacketWriter &addMessage(const Message &msg) {
l@270:     if (storage.size() != 0 && bundles.empty()) OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES);
l@270:     else msg.packMessage(storage, bundles.size()>0);
l@270:     if (!msg.isOk()) OSCPKT_SET_ERR(msg.getErr());
l@270:     return *this;
l@270:   }
l@270: 
l@270:   /** the error flag will be raised if an opened bundle is not closed, or if more than one message is
l@270:       inserted in the packet without a bundle */
l@270:   bool isOk() { return err == OK_NO_ERROR; }
l@270:   ErrorCode getErr() { return err; }
l@270: 
l@270:   /** return the number of bytes of the osc packet -- will always be a
l@270:       multiple of 4 -- returns 0 if the construction of the packet has
l@270:       failed. */
l@270:   uint32_t packetSize() { return err ? 0 : (uint32_t)storage.size(); }
l@270:   
l@270:   /** return the bytes of the osc packet (NULL if the construction of the packet has failed) */
l@270:   char *packetData() { return err ? 0 : storage.begin(); }
l@270: private:  
l@270:   std::vector<size_t> bundles; // hold the position in the storage array of the beginning marker of each bundle
l@270:   Storage storage;
l@270:   ErrorCode err;
l@270: };
l@270: 
l@270: // see the OSC spec for the precise pattern matching rules
l@270: inline const char *internalPatternMatch(const char *pattern, const char *path) {
l@270:   while (*pattern) {
l@270:     const char *p = pattern;
l@270:     if (*p == '?' && *path) { ++p; ++path; }
l@270:     else if (*p == '[' && *path) { // bracketted range, e.g. [a-zABC]
l@270:       ++p;
l@270:       bool reverse = false;
l@270:       if (*p == '!') { reverse = true; ++p; }
l@270:       bool match = reverse;
l@270:       for (; *p && *p != ']'; ++p) {
l@270:         char c0 = *p, c1 = c0;
l@270:         if (p[1] == '-' && p[2] && p[2] != ']') { p += 2; c1 = *p; }
l@270:         if (*path >= c0 && *path <= c1) { match = !reverse; }
l@270:       }
l@270:       if (!match || *p != ']') return pattern;
l@270:       ++p; ++path;
l@270:     } else if (*p == '*') { // wildcard '*'
l@270:       while (*p == '*') ++p; 
l@270:       const char *best = 0;
l@270:       while (true) {
l@270:         const char *ret = internalPatternMatch(p, path);
l@270:         if (ret && ret > best) best = ret;
l@270:         if (*path == 0 || *path == '/') break;
l@270:         else ++path;
l@270:       }
l@270:       return best;
l@270:     } else if (*p == '/' && *(p+1) == '/') { // the super-wildcard '//'
l@270:       while (*(p+1)=='/') ++p;
l@270:       const char *best = 0;
l@270:       while (true) {
l@270:         const char *ret = internalPatternMatch(p, path);
l@270:         if (ret && ret > best) best = ret;
l@270:         if (*path == 0) break;
l@270:         if (*path == 0 || (path = strchr(path+1, '/')) == 0) break;
l@270:       }      
l@270:       return best;
l@270:     } else if (*p == '{') { // braced list {foo,bar,baz}
l@270:       const char *end = strchr(p, '}'), *q;
l@270:       if (!end) return 0; // syntax error in brace list..
l@270:       bool match = false;
l@270:       do {
l@270:         ++p;
l@270:         q = strchr(p, ',');
l@270:         if (q == 0 || q > end) q = end;
l@270:         if (strncmp(p, path, q-p)==0) {
l@270:           path += (q-p); p = end+1; match = true;
l@270:         } else p=q;
l@270:       } while (q != end && !match);
l@270:       if (!match) return pattern;
l@270:     } else if (*p == *path) { ++p; ++path; } // any other character
l@270:     else break;
l@270:     pattern = p;
l@270:   }
l@270:   return (*path == 0 ? pattern : 0);
l@270: }
l@270: 
l@270: inline bool partialPatternMatch(const std::string &pattern, const std::string &test) {
l@270:   const char *q = internalPatternMatch(pattern.c_str(), test.c_str());
l@270:   return q != 0;
l@270: }
l@270: 
l@270: inline bool fullPatternMatch(const std::string &pattern, const std::string &test) {
l@270:   const char *q = internalPatternMatch(pattern.c_str(), test.c_str());
l@270:   return q && *q == 0;
l@270: }
l@270: 
l@270: } // namespace oscpkt
l@270: 
l@270: #endif // OSCPKT_HH