annotate include/oscpkt.hh @ 531:ddb86944e138 prerelease

cleaned up all pd examples (and removed some). added polysynth and vangelisiser to instruments examples
author chnrx <chris.heinrichs@gmail.com>
date Thu, 23 Jun 2016 20:40:05 +0100
parents de37582ce6f3
children
rev   line source
l@270 1 /** @mainpage OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library
l@270 2
l@270 3 Before using this file please take the time to read the OSC spec, it
l@270 4 is short and not complicated: http://opensoundcontrol.org/spec-1_0
l@270 5
l@270 6 Features:
l@270 7 - handles basic OSC types: TFihfdsb
l@270 8 - handles bundles
l@270 9 - handles OSC pattern-matching rules (wildcards etc in message paths)
l@270 10 - portable on win / macos / linux
l@270 11 - robust wrt malformed packets
l@270 12 - optional udp transport for packets
l@270 13 - concise, all in a single .h file
l@270 14 - does not throw exceptions
l@270 15
l@270 16 does not:
l@270 17 - take into account timestamp values.
l@270 18 - provide a cpu-scalable message dispatching.
l@270 19 - not suitable for use inside a realtime thread as it allocates memory when
l@270 20 building or reading messages.
l@270 21
l@270 22
l@270 23 There are basically 3 classes of interest:
l@270 24 - oscpkt::Message : read/write the content of an OSC message
l@270 25 - oscpkt::PacketReader : read the bundles/messages embedded in an OSC packet
l@270 26 - oscpkt::PacketWriter : write bundles/messages into an OSC packet
l@270 27
l@270 28 And optionaly:
l@270 29 - oscpkt::UdpSocket : read/write OSC packets over UDP.
l@270 30
l@270 31 @example: oscpkt_demo.cc
l@270 32 @example: oscpkt_test.cc
l@270 33 */
l@270 34
l@270 35 /* Copyright (C) 2010 Julien Pommier
l@270 36
l@270 37 This software is provided 'as-is', without any express or implied
l@270 38 warranty. In no event will the authors be held liable for any damages
l@270 39 arising from the use of this software.
l@270 40
l@270 41 Permission is granted to anyone to use this software for any purpose,
l@270 42 including commercial applications, and to alter it and redistribute it
l@270 43 freely, subject to the following restrictions:
l@270 44
l@270 45 1. The origin of this software must not be misrepresented; you must not
l@270 46 claim that you wrote the original software. If you use this software
l@270 47 in a product, an acknowledgment in the product documentation would be
l@270 48 appreciated but is not required.
l@270 49 2. Altered source versions must be plainly marked as such, and must not be
l@270 50 misrepresented as being the original software.
l@270 51 3. This notice may not be removed or altered from any source distribution.
l@270 52
l@270 53 (this is the zlib license)
l@270 54 */
l@270 55
l@270 56 #ifndef OSCPKT_HH
l@270 57 #define OSCPKT_HH
l@270 58
l@270 59 #ifndef _MSC_VER
l@270 60 #include <stdint.h>
l@270 61 #else
l@270 62 namespace oscpkt {
l@270 63 typedef __int32 int32_t;
l@270 64 typedef unsigned __int32 uint32_t;
l@270 65 typedef __int64 int64_t;
l@270 66 typedef unsigned __int64 uint64_t;
l@270 67 }
l@270 68 #endif
l@270 69 #include <cstring>
l@270 70 #include <cassert>
l@270 71 #include <string>
l@270 72 #include <vector>
l@270 73 #include <list>
l@270 74
l@270 75 #if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST)
l@270 76 #include <iostream>
l@270 77 #endif
l@270 78
l@270 79 namespace oscpkt {
l@270 80
l@270 81 /**
l@270 82 OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second.
l@270 83 */
l@270 84 class TimeTag {
l@270 85 uint64_t v;
l@270 86 public:
l@270 87 TimeTag() : v(1) {}
l@270 88 explicit TimeTag(uint64_t w): v(w) {}
l@270 89 operator uint64_t() const { return v; }
l@270 90 static TimeTag immediate() { return TimeTag(1); }
l@270 91 };
l@270 92
l@270 93 /* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */
l@270 94 enum {
l@270 95 TYPE_TAG_TRUE = 'T',
l@270 96 TYPE_TAG_FALSE = 'F',
l@270 97 TYPE_TAG_INT32 = 'i',
l@270 98 TYPE_TAG_INT64 = 'h',
l@270 99 TYPE_TAG_FLOAT = 'f',
l@270 100 TYPE_TAG_DOUBLE = 'd',
l@270 101 TYPE_TAG_STRING = 's',
l@270 102 TYPE_TAG_BLOB = 'b'
l@270 103 };
l@270 104
l@270 105 /* a few utility functions follow.. */
l@270 106
l@270 107 // round to the next multiple of 4, works for size_t and pointer arguments
l@270 108 template <typename Type> Type ceil4(Type p) { return (Type)((size_t(p) + 3)&(~size_t(3))); }
l@270 109
l@270 110 // check that a memory area is zero padded until the next address which is a multiple of 4
l@270 111 inline bool isZeroPaddingCorrect(const char *p) {
l@270 112 const char *q = ceil4(p);
l@270 113 for (;p < q; ++p)
l@270 114 if (*p != 0) { return false; }
l@270 115 return true;
l@270 116 }
l@270 117
l@270 118 // stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes.
l@270 119 template <typename POD> union PodBytes {
l@270 120 char bytes[sizeof(POD)];
l@270 121 POD value;
l@270 122 };
l@270 123
l@270 124 inline bool isBigEndian() { // a compile-time constant would certainly improve performances..
l@270 125 PodBytes<int32_t> p; p.value = 0x12345678;
l@270 126 return p.bytes[0] == 0x12;
l@270 127 }
l@270 128
l@270 129 /** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */
l@270 130 template <typename POD> POD bytes2pod(const char *bytes) {
l@270 131 PodBytes<POD> p;
l@270 132 for (size_t i=0; i < sizeof(POD); ++i) {
l@270 133 if (isBigEndian())
l@270 134 p.bytes[i] = bytes[i];
l@270 135 else
l@270 136 p.bytes[i] = bytes[sizeof(POD) - i - 1];
l@270 137 }
l@270 138 return p.value;
l@270 139 }
l@270 140
l@270 141 /** stored a POD type into an unaligned bytes array, using little endian representation */
l@270 142 template <typename POD> void pod2bytes(const POD value, char *bytes) {
l@270 143 PodBytes<POD> p; p.value = value;
l@270 144 for (size_t i=0; i < sizeof(POD); ++i) {
l@270 145 if (isBigEndian())
l@270 146 bytes[i] = p.bytes[i];
l@270 147 else
l@270 148 bytes[i] = p.bytes[sizeof(POD) - i - 1];
l@270 149 }
l@270 150 }
l@270 151
l@270 152 /** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */
l@270 153 struct Storage {
l@270 154 std::vector<char> data;
l@270 155 Storage() { data.reserve(200); }
l@270 156 char *getBytes(size_t sz) {
l@270 157 assert((data.size() & 3) == 0);
l@270 158 if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz)*2); }
l@270 159 size_t sz4 = ceil4(sz);
l@270 160 size_t pos = data.size();
l@270 161 data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK
l@270 162 return &(data[pos]);
l@270 163 }
l@270 164 char *begin() { return data.size() ? &data.front() : 0; }
l@270 165 char *end() { return begin() + size(); }
l@270 166 const char *begin() const { return data.size() ? &data.front() : 0; }
l@270 167 const char *end() const { return begin() + size(); }
l@270 168 size_t size() const { return data.size(); }
l@270 169 void assign(const char *beg, const char *end) { data.assign(beg, end); }
l@270 170 void clear() { data.resize(0); }
l@270 171 };
l@270 172
l@270 173 /** check if the path matches the supplied path pattern , according to the OSC spec pattern
l@270 174 rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */
l@270 175 bool fullPatternMatch(const std::string &pattern, const std::string &path);
l@270 176 /** check if the path matches the beginning of pattern */
l@270 177 bool partialPatternMatch(const std::string &pattern, const std::string &path);
l@270 178
l@270 179 #if defined(OSCPKT_DEBUG)
l@270 180 #define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0)
l@270 181 #else
l@270 182 #define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0)
l@270 183 #endif
l@270 184
l@270 185 typedef enum { OK_NO_ERROR=0,
l@270 186 // errors raised by the Message class:
l@270 187 MALFORMED_ADDRESS_PATTERN, MALFORMED_TYPE_TAGS, MALFORMED_ARGUMENTS, UNHANDLED_TYPE_TAGS,
l@270 188 // errors raised by ArgReader
l@270 189 TYPE_MISMATCH, NOT_ENOUGH_ARG, PATTERN_MISMATCH,
l@270 190 // errors raised by PacketReader/PacketWriter
l@270 191 INVALID_BUNDLE, INVALID_PACKET_SIZE, BUNDLE_REQUIRED_FOR_MULTI_MESSAGES } ErrorCode;
l@270 192
l@270 193 /**
l@270 194 struct used to hold an OSC message that will be written or read.
l@270 195
l@270 196 The list of arguments is exposed as a sort of queue. You "pop"
l@270 197 arguments from the front of the queue when reading, you push
l@270 198 arguments at the back of the queue when writing.
l@270 199
l@270 200 Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")...
l@270 201
l@270 202 Example of use:
l@270 203
l@270 204 creation of a message:
l@270 205 @code
l@270 206 msg.init("/foo").pushInt32(4).pushStr("bar");
l@270 207 @endcode
l@270 208 reading a message, with error detection:
l@270 209 @code
l@270 210 if (msg.match("/foo/b*ar/plop")) {
l@270 211 int i; std::string s; std::vector<char> b;
l@270 212 if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) {
l@270 213 process message...;
l@270 214 } else arguments mismatch;
l@270 215 }
l@270 216 @endcode
l@270 217 */
l@270 218 class Message {
l@270 219 TimeTag time_tag;
l@270 220 std::string address;
l@270 221 std::string type_tags;
l@270 222 std::vector<std::pair<size_t, size_t> > arguments; // array of pairs (pos,size), pos being an index into the 'storage' array.
l@270 223 Storage storage; // the arguments data is stored here
l@270 224 ErrorCode err;
l@270 225 public:
l@270 226 /** ArgReader is used for popping arguments from a Message, holds a
l@270 227 pointer to the original Message, and maintains a local error code */
l@270 228 class ArgReader {
l@270 229 const Message *msg;
l@270 230 ErrorCode err;
l@270 231 size_t arg_idx; // arg index of the next arg that will be popped out.
l@270 232 public:
l@270 233 ArgReader(const Message &m, ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0) {
l@270 234 if (e != OK_NO_ERROR && err == OK_NO_ERROR) err=e;
l@270 235 }
l@270 236 ArgReader(const ArgReader &other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {}
l@270 237 bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; }
l@270 238 bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; }
l@270 239 bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; }
l@270 240 bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; }
l@270 241 bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; }
l@270 242 bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; }
l@270 243 bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; }
l@270 244
l@270 245 size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; }
l@270 246 bool isOk() const { return err == OK_NO_ERROR; }
l@270 247 operator bool() const { return isOk(); } // implicit bool conversion is handy here
l@270 248 /** call this at the end of the popXXX() chain to make sure everything is ok and
l@270 249 all arguments have been popped */
l@270 250 bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; }
l@270 251 ErrorCode getErr() const { return err; }
l@270 252
l@270 253 /** retrieve an int32 argument */
l@270 254 ArgReader &popInt32(int32_t &i) { return popPod<int32_t>(TYPE_TAG_INT32, i); }
l@270 255 /** retrieve an int64 argument */
l@270 256 ArgReader &popInt64(int64_t &i) { return popPod<int64_t>(TYPE_TAG_INT64, i); }
l@270 257 /** retrieve a single precision floating point argument */
l@270 258 ArgReader &popFloat(float &f) { return popPod<float>(TYPE_TAG_FLOAT, f); }
l@270 259 /** retrieve a double precision floating point argument */
l@270 260 ArgReader &popDouble(double &d) { return popPod<double>(TYPE_TAG_DOUBLE, d); }
l@270 261 /** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */
l@270 262 ArgReader &popStr(std::string &s) {
l@270 263 if (precheck(TYPE_TAG_STRING)) {
l@270 264 s = argBeg(arg_idx++);
l@270 265 }
l@270 266 return *this;
l@270 267 }
l@270 268 /** retrieve a binary blob */
l@270 269 ArgReader &popBlob(std::vector<char> &b) {
l@270 270 if (precheck(TYPE_TAG_BLOB)) {
l@270 271 b.assign(argBeg(arg_idx)+4, argEnd(arg_idx));
l@270 272 ++arg_idx;
l@270 273 }
l@270 274 return *this;
l@270 275 }
l@270 276 /** retrieve a boolean argument */
l@270 277 ArgReader &popBool(bool &b) {
l@270 278 b = false;
l@270 279 if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
l@270 280 else if (currentTypeTag() == TYPE_TAG_TRUE) b = true;
l@270 281 else if (currentTypeTag() == TYPE_TAG_FALSE) b = false;
l@270 282 else OSCPKT_SET_ERR(TYPE_MISMATCH);
l@270 283 ++arg_idx;
l@270 284 return *this;
l@270 285 }
l@270 286 /** skip whatever comes next */
l@270 287 ArgReader &pop() {
l@270 288 if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
l@270 289 else ++arg_idx;
l@270 290 return *this;
l@270 291 }
l@270 292 private:
l@270 293 const char *argBeg(size_t idx) {
l@270 294 if (err || idx >= msg->arguments.size()) return 0;
l@270 295 else return msg->storage.begin() + msg->arguments[idx].first;
l@270 296 }
l@270 297 const char *argEnd(size_t idx) {
l@270 298 if (err || idx >= msg->arguments.size()) return 0;
l@270 299 else return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second;
l@270 300 }
l@270 301 int currentTypeTag() {
l@270 302 if (!err && arg_idx < msg->type_tags.size()) return msg->type_tags[arg_idx];
l@270 303 else OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
l@270 304 return -1;
l@270 305 }
l@270 306 template <typename POD> ArgReader &popPod(int tag, POD &v) {
l@270 307 if (precheck(tag)) {
l@270 308 v = bytes2pod<POD>(argBeg(arg_idx));
l@270 309 ++arg_idx;
l@270 310 } else v = POD(0);
l@270 311 return *this;
l@270 312 }
l@270 313 /* pre-check stuff before popping an argument from the message */
l@270 314 bool precheck(int tag) {
l@270 315 if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
l@270 316 else if (!err && currentTypeTag() != tag) OSCPKT_SET_ERR(TYPE_MISMATCH);
l@270 317 return err == OK_NO_ERROR;
l@270 318 }
l@270 319 };
l@270 320
l@270 321 Message() { clear(); }
l@270 322 Message(const std::string &s, TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {}
l@270 323 Message(const void *ptr, size_t sz, TimeTag tt = TimeTag::immediate()) { buildFromRawData(ptr, sz); time_tag = tt; }
l@270 324
l@270 325 bool isOk() const { return err == OK_NO_ERROR; }
l@270 326 ErrorCode getErr() const { return err; }
l@270 327
l@270 328 /** return the type_tags string, with its initial ',' stripped. */
l@270 329 const std::string &typeTags() const { return type_tags; }
l@270 330 /** retrieve the address pattern. If you want to follow to the whole OSC spec, you
l@270 331 have to handle its matching rules for address specifications -- this file does
l@270 332 not provide this functionality */
l@270 333 const std::string &addressPattern() const { return address; }
l@270 334 TimeTag timeTag() const { return time_tag; }
l@270 335 /** clear the message and start a new message with the supplied address and time_tag. */
l@270 336 Message &init(const std::string &addr, TimeTag tt = TimeTag::immediate()) {
l@270 337 clear();
l@270 338 address = addr; time_tag = tt;
l@270 339 if (address.empty() || address[0] != '/') OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN);
l@270 340 return *this;
l@270 341 }
l@270 342
l@270 343 /** start a matching test. The typical use-case is to follow this by
l@270 344 a sequence of calls to popXXX() and a final call to
l@270 345 isOkNoMoreArgs() which will allow to check that everything went
l@270 346 fine. For example:
l@270 347 @code
l@270 348 if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); }
l@270 349 else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); }
l@270 350 else cerr << "unhandled message: " << msg << "\n";
l@270 351 @endcode
l@270 352 */
l@270 353 ArgReader match(const std::string &test) const {
l@270 354 return ArgReader(*this, fullPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH);
l@270 355 }
l@270 356 /** return true if the 'test' path matched by the first characters of addressPattern().
l@270 357 For ex. ("/foo/bar").partialMatch("/foo/") is true */
l@270 358 ArgReader partialMatch(const std::string &test) const {
l@270 359 return ArgReader(*this, partialPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH);
l@270 360 }
l@270 361 ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); }
l@270 362
l@270 363 /** build the osc message for raw data (the message will keep a copy of that data) */
l@270 364 void buildFromRawData(const void *ptr, size_t sz) {
l@270 365 clear();
l@270 366 storage.assign((const char*)ptr, (const char*)ptr + sz);
l@270 367 const char *address_beg = storage.begin();
l@270 368 const char *address_end = (const char*)memchr(address_beg, 0, storage.end()-address_beg);
l@270 369 if (!address_end || !isZeroPaddingCorrect(address_end+1) || address_beg[0] != '/') {
l@270 370 OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); return;
l@270 371 } else address.assign(address_beg, address_end);
l@270 372
l@270 373 const char *type_tags_beg = ceil4(address_end+1);
l@270 374 const char *type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end()-type_tags_beg);
l@270 375 if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end+1) || type_tags_beg[0] != ',') {
l@270 376 OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS); return;
l@270 377 } else type_tags.assign(type_tags_beg+1, type_tags_end); // we do not copy the initial ','
l@270 378
l@270 379 const char *arg = ceil4(type_tags_end+1); assert(arg <= storage.end());
l@270 380 size_t iarg = 0;
l@270 381 while (isOk() && iarg < type_tags.size()) {
l@270 382 assert(arg <= storage.end());
l@270 383 size_t len = getArgSize(type_tags[iarg], arg);
l@270 384 if (isOk()) arguments.push_back(std::make_pair(arg - storage.begin(), len));
l@270 385 arg += ceil4(len); ++iarg;
l@270 386 }
l@270 387 if (iarg < type_tags.size() || arg != storage.end()) {
l@270 388 OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
l@270 389 }
l@270 390 }
l@270 391
l@270 392 /* below are all the functions that serve when *writing* a message */
l@270 393 Message &pushBool(bool b) {
l@270 394 type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE);
l@270 395 arguments.push_back(std::make_pair(storage.size(), storage.size()));
l@270 396 return *this;
l@270 397 }
l@270 398 Message &pushInt32(int32_t i) { return pushPod(TYPE_TAG_INT32, i); }
l@270 399 Message &pushInt64(int64_t h) { return pushPod(TYPE_TAG_INT64, h); }
l@270 400 Message &pushFloat(float f) { return pushPod(TYPE_TAG_FLOAT, f); }
l@270 401 Message &pushDouble(double d) { return pushPod(TYPE_TAG_DOUBLE, d); }
l@270 402 Message &pushStr(const std::string &s) {
l@270 403 assert(s.size() < 2147483647); // insane values are not welcome
l@270 404 type_tags += TYPE_TAG_STRING;
l@270 405 arguments.push_back(std::make_pair(storage.size(), s.size() + 1));
l@270 406 strcpy(storage.getBytes(s.size()+1), s.c_str());
l@270 407 return *this;
l@270 408 }
l@270 409 Message &pushBlob(void *ptr, size_t num_bytes) {
l@270 410 assert(num_bytes < 2147483647); // insane values are not welcome
l@270 411 type_tags += TYPE_TAG_BLOB;
l@270 412 arguments.push_back(std::make_pair(storage.size(), num_bytes+4));
l@270 413 pod2bytes<int32_t>((int32_t)num_bytes, storage.getBytes(4));
l@270 414 if (num_bytes)
l@270 415 memcpy(storage.getBytes(num_bytes), ptr, num_bytes);
l@270 416 return *this;
l@270 417 }
l@270 418
l@270 419 /** reset the message to a clean state */
l@270 420 void clear() {
l@270 421 address.clear(); type_tags.clear(); storage.clear(); arguments.clear();
l@270 422 err = OK_NO_ERROR; time_tag = TimeTag::immediate();
l@270 423 }
l@270 424
l@270 425 /** write the raw message data (used by PacketWriter) */
l@270 426 void packMessage(Storage &s, bool write_size) const {
l@270 427 if (!isOk()) return;
l@270 428 size_t l_addr = address.size()+1, l_type = type_tags.size()+2;
l@270 429 if (write_size)
l@270 430 pod2bytes<uint32_t>(uint32_t(ceil4(l_addr) + ceil4(l_type) + ceil4(storage.size())), s.getBytes(4));
l@270 431 strcpy(s.getBytes(l_addr), address.c_str());
l@270 432 strcpy(s.getBytes(l_type), ("," + type_tags).c_str());
l@270 433 if (storage.size())
l@270 434 memcpy(s.getBytes(storage.size()), const_cast<Storage&>(storage).begin(), storage.size());
l@270 435 }
l@270 436
l@270 437 private:
l@270 438
l@270 439 /* get the number of bytes occupied by the argument */
l@270 440 size_t getArgSize(int type, const char *p) {
l@270 441 if (err) return 0;
l@270 442 size_t sz = 0;
l@270 443 assert(p >= storage.begin() && p <= storage.end());
l@270 444 switch (type) {
l@270 445 case TYPE_TAG_TRUE:
l@270 446 case TYPE_TAG_FALSE: sz = 0; break;
l@270 447 case TYPE_TAG_INT32:
l@270 448 case TYPE_TAG_FLOAT: sz = 4; break;
l@270 449 case TYPE_TAG_INT64:
l@270 450 case TYPE_TAG_DOUBLE: sz = 8; break;
l@270 451 case TYPE_TAG_STRING: {
l@270 452 const char *q = (const char*)memchr(p, 0, storage.end()-p);
l@270 453 if (!q) OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
l@270 454 else sz = (q-p)+1;
l@270 455 } break;
l@270 456 case TYPE_TAG_BLOB: {
l@270 457 if (p == storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
l@270 458 sz = 4+bytes2pod<uint32_t>(p);
l@270 459 } break;
l@270 460 default: {
l@270 461 OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS); return 0;
l@270 462 } break;
l@270 463 }
l@270 464 if (p+sz > storage.end() || /* string or blob too large.. */
l@270 465 p+sz < p /* or even blob so large that it did overflow */) {
l@270 466 OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0;
l@270 467 }
l@270 468 if (!isZeroPaddingCorrect(p+sz)) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
l@270 469 return sz;
l@270 470 }
l@270 471
l@270 472 template <typename POD> Message &pushPod(int tag, POD v) {
l@270 473 type_tags += (char)tag;
l@270 474 arguments.push_back(std::make_pair(storage.size(), sizeof(POD)));
l@270 475 pod2bytes(v, storage.getBytes(sizeof(POD)));
l@270 476 return *this;
l@270 477 }
l@270 478
l@270 479 #ifdef OSCPKT_OSTREAM_OUTPUT
l@270 480 friend std::ostream &operator<<(std::ostream &os, const Message &msg) {
l@270 481 os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=[";
l@270 482 Message::ArgReader arg(msg);
l@270 483 while (arg.nbArgRemaining() && arg.isOk()) {
l@270 484 if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); }
l@270 485 else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; }
l@270 486 else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; }
l@270 487 else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; }
l@270 488 else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; }
l@270 489 else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; }
l@270 490 else if (arg.isBlob()) { std::vector<char> b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; }
l@270 491 else {
l@270 492 assert(0); // I forgot a case..
l@270 493 }
l@270 494 if (arg.nbArgRemaining()) os << ", ";
l@270 495 }
l@270 496 if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); }
l@270 497 os << "]";
l@270 498 return os;
l@270 499 }
l@270 500 #endif
l@270 501 };
l@270 502
l@270 503 /**
l@270 504 parse an OSC packet and extracts the embedded OSC messages.
l@270 505 */
l@270 506 class PacketReader {
l@270 507 public:
l@270 508 PacketReader() { err = OK_NO_ERROR; }
l@270 509 /** pointer and size of the osc packet to be parsed. */
l@270 510 PacketReader(const void *ptr, size_t sz) { init(ptr, sz); }
l@270 511
l@270 512 void init(const void *ptr, size_t sz) {
l@270 513 err = OK_NO_ERROR; messages.clear();
l@270 514 if ((sz%4) == 0) {
l@270 515 parse((const char*)ptr, (const char *)ptr+sz, TimeTag::immediate());
l@270 516 } else OSCPKT_SET_ERR(INVALID_PACKET_SIZE);
l@270 517 it_messages = messages.begin();
l@270 518 }
l@270 519
l@270 520 /** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */
l@270 521 Message *popMessage() {
l@270 522 if (!err && !messages.empty() && it_messages != messages.end()) return &*it_messages++;
l@270 523 else return 0;
l@270 524 }
l@270 525 bool isOk() const { return err == OK_NO_ERROR; }
l@270 526 ErrorCode getErr() const { return err; }
l@270 527
l@270 528 private:
l@270 529 std::list<Message> messages;
l@270 530 std::list<Message>::iterator it_messages;
l@270 531 ErrorCode err;
l@270 532
l@270 533 void parse(const char *beg, const char *end, TimeTag time_tag) {
l@270 534 assert(beg <= end && !err); assert(((end-beg)%4)==0);
l@270 535
l@270 536 if (beg == end) return;
l@270 537 if (*beg == '#') {
l@270 538 /* it's a bundle */
l@270 539 if (end - beg >= 20
l@270 540 && memcmp(beg, "#bundle\0", 8) == 0) {
l@270 541 TimeTag time_tag2(bytes2pod<uint64_t>(beg+8));
l@270 542 const char *pos = beg + 16;
l@270 543 do {
l@270 544 uint32_t sz = bytes2pod<uint32_t>(pos); pos += 4;
l@270 545 if ((sz&3) != 0 || pos + sz > end || pos+sz < pos) {
l@270 546 OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270 547 } else {
l@270 548 parse(pos, pos+sz, time_tag2);
l@270 549 pos += sz;
l@270 550 }
l@270 551 } while (!err && pos != end);
l@270 552 } else {
l@270 553 OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270 554 }
l@270 555 } else {
l@270 556 messages.push_back(Message(beg, end-beg, time_tag));
l@270 557 if (!messages.back().isOk()) OSCPKT_SET_ERR(messages.back().getErr());
l@270 558 }
l@270 559 }
l@270 560 };
l@270 561
l@270 562
l@270 563 /**
l@270 564 Assemble messages into an OSC packet. Example of use:
l@270 565 @code
l@270 566 PacketWriter pkt;
l@270 567 Message msg;
l@270 568 pkt.startBundle();
l@270 569 pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f));
l@270 570 pkt.addMessage(msg.init("/bar").pushBool(false));
l@270 571 pkt.endBundle();
l@270 572 if (pkt.isOk()) {
l@270 573 send(pkt.data(), pkt.size());
l@270 574 }
l@270 575 @endcode
l@270 576 */
l@270 577 class PacketWriter {
l@270 578 public:
l@270 579 PacketWriter() { init(); }
l@270 580 PacketWriter &init() { err = OK_NO_ERROR; storage.clear(); bundles.clear(); return *this; }
l@270 581
l@270 582 /** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to
l@270 583 put them in a bundle. Nested bundles inside bundles are also allowed. */
l@270 584 PacketWriter &startBundle(TimeTag ts = TimeTag::immediate()) {
l@270 585 char *p;
l@270 586 if (bundles.size()) storage.getBytes(4); // hold the bundle size
l@270 587 p = storage.getBytes(8); strcpy(p, "#bundle"); bundles.push_back(p - storage.begin());
l@270 588 p = storage.getBytes(8); pod2bytes<uint64_t>(ts, p);
l@270 589 return *this;
l@270 590 }
l@270 591 /** close the current bundle. */
l@270 592 PacketWriter &endBundle() {
l@270 593 if (bundles.size()) {
l@270 594 if (storage.size() - bundles.back() == 16) {
l@270 595 pod2bytes<uint32_t>(0, storage.getBytes(4)); // the 'empty bundle' case, not very elegant
l@270 596 }
l@270 597 if (bundles.size()>1) { // no size stored for the top-level bundle
l@270 598 pod2bytes<uint32_t>(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back()-4);
l@270 599 }
l@270 600 bundles.pop_back();
l@270 601 } else OSCPKT_SET_ERR(INVALID_BUNDLE);
l@270 602 return *this;
l@270 603 }
l@270 604
l@270 605 /** insert an Osc message into the current bundle / packet.
l@270 606 */
l@270 607 PacketWriter &addMessage(const Message &msg) {
l@270 608 if (storage.size() != 0 && bundles.empty()) OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES);
l@270 609 else msg.packMessage(storage, bundles.size()>0);
l@270 610 if (!msg.isOk()) OSCPKT_SET_ERR(msg.getErr());
l@270 611 return *this;
l@270 612 }
l@270 613
l@270 614 /** the error flag will be raised if an opened bundle is not closed, or if more than one message is
l@270 615 inserted in the packet without a bundle */
l@270 616 bool isOk() { return err == OK_NO_ERROR; }
l@270 617 ErrorCode getErr() { return err; }
l@270 618
l@270 619 /** return the number of bytes of the osc packet -- will always be a
l@270 620 multiple of 4 -- returns 0 if the construction of the packet has
l@270 621 failed. */
l@270 622 uint32_t packetSize() { return err ? 0 : (uint32_t)storage.size(); }
l@270 623
l@270 624 /** return the bytes of the osc packet (NULL if the construction of the packet has failed) */
l@270 625 char *packetData() { return err ? 0 : storage.begin(); }
l@270 626 private:
l@270 627 std::vector<size_t> bundles; // hold the position in the storage array of the beginning marker of each bundle
l@270 628 Storage storage;
l@270 629 ErrorCode err;
l@270 630 };
l@270 631
l@270 632 // see the OSC spec for the precise pattern matching rules
l@270 633 inline const char *internalPatternMatch(const char *pattern, const char *path) {
l@270 634 while (*pattern) {
l@270 635 const char *p = pattern;
l@270 636 if (*p == '?' && *path) { ++p; ++path; }
l@270 637 else if (*p == '[' && *path) { // bracketted range, e.g. [a-zABC]
l@270 638 ++p;
l@270 639 bool reverse = false;
l@270 640 if (*p == '!') { reverse = true; ++p; }
l@270 641 bool match = reverse;
l@270 642 for (; *p && *p != ']'; ++p) {
l@270 643 char c0 = *p, c1 = c0;
l@270 644 if (p[1] == '-' && p[2] && p[2] != ']') { p += 2; c1 = *p; }
l@270 645 if (*path >= c0 && *path <= c1) { match = !reverse; }
l@270 646 }
l@270 647 if (!match || *p != ']') return pattern;
l@270 648 ++p; ++path;
l@270 649 } else if (*p == '*') { // wildcard '*'
l@270 650 while (*p == '*') ++p;
l@270 651 const char *best = 0;
l@270 652 while (true) {
l@270 653 const char *ret = internalPatternMatch(p, path);
l@270 654 if (ret && ret > best) best = ret;
l@270 655 if (*path == 0 || *path == '/') break;
l@270 656 else ++path;
l@270 657 }
l@270 658 return best;
l@270 659 } else if (*p == '/' && *(p+1) == '/') { // the super-wildcard '//'
l@270 660 while (*(p+1)=='/') ++p;
l@270 661 const char *best = 0;
l@270 662 while (true) {
l@270 663 const char *ret = internalPatternMatch(p, path);
l@270 664 if (ret && ret > best) best = ret;
l@270 665 if (*path == 0) break;
l@270 666 if (*path == 0 || (path = strchr(path+1, '/')) == 0) break;
l@270 667 }
l@270 668 return best;
l@270 669 } else if (*p == '{') { // braced list {foo,bar,baz}
l@270 670 const char *end = strchr(p, '}'), *q;
l@270 671 if (!end) return 0; // syntax error in brace list..
l@270 672 bool match = false;
l@270 673 do {
l@270 674 ++p;
l@270 675 q = strchr(p, ',');
l@270 676 if (q == 0 || q > end) q = end;
l@270 677 if (strncmp(p, path, q-p)==0) {
l@270 678 path += (q-p); p = end+1; match = true;
l@270 679 } else p=q;
l@270 680 } while (q != end && !match);
l@270 681 if (!match) return pattern;
l@270 682 } else if (*p == *path) { ++p; ++path; } // any other character
l@270 683 else break;
l@270 684 pattern = p;
l@270 685 }
l@270 686 return (*path == 0 ? pattern : 0);
l@270 687 }
l@270 688
l@270 689 inline bool partialPatternMatch(const std::string &pattern, const std::string &test) {
l@270 690 const char *q = internalPatternMatch(pattern.c_str(), test.c_str());
l@270 691 return q != 0;
l@270 692 }
l@270 693
l@270 694 inline bool fullPatternMatch(const std::string &pattern, const std::string &test) {
l@270 695 const char *q = internalPatternMatch(pattern.c_str(), test.c_str());
l@270 696 return q && *q == 0;
l@270 697 }
l@270 698
l@270 699 } // namespace oscpkt
l@270 700
l@270 701 #endif // OSCPKT_HH