comparison include/oscpkt.hh @ 270:de37582ce6f3 prerelease

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