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 module more.osc;
56 
57 import std.string : format;
58 import std.c.string : strchr,strncmp;
59 
60 version(unittest)
61 {
62   import std.stdio;
63 }
64 
65 
66 class OscException : Exception
67 {
68   this(string msg)
69   {
70     super(msg);
71   }
72 }
73 
74 struct OscMessage
75 {
76   const(char)[] address; // Note: that this address is null terminated
77 
78   void parse(const(ubyte)[] data)
79   {
80     if(data.length < 4)
81       throw new OscException("an OscPacket must be at least 4 bytes long");
82     if( (data.length & 0x03) != 0 )
83       throw new OscException("an OscPacket length must be a multiple of 4");
84 
85     if(data[0] != '/')
86       throw new OscException(format("an Osc request must start with the '/' character but it started with '%s'", data[0]));
87     
88     size_t checkIndex = 3;
89     while(true) {
90       if(data[checkIndex] == 0) break;
91       checkIndex += 4;
92       if(checkIndex >= data.length)
93 	throw new OscException("missing null character to end osc string");
94     }
95 
96     do {
97       if(data[checkIndex - 1] != 0) break;
98       checkIndex--;
99     } while(checkIndex > 0);
100 
101     this.address = cast(const(char)[])data[0..checkIndex];
102 
103     //throw new Exception("implement");
104   }
105   unittest
106   {
107     OscMessage message;
108 
109     message.parse([cast(ubyte)'/',0,0,0]);
110     assert(message.address == "/");
111 
112     message.parse([cast(ubyte)'/','a',0,0]);
113     assert(message.address == "/a");
114 
115     message.parse([cast(ubyte)'/','a','b',0]);
116     assert(message.address == "/ab");
117 
118     message.parse([cast(ubyte)'/','a','b','c',0,0,0,0]);
119     assert(message.address == "/abc");
120 
121     message.parse([cast(ubyte)'/','a','b','c','d',0,0,0]);
122     assert(message.address == "/abcd");
123 
124     message.parse([cast(ubyte)'/','a','b','c','d','e',0,0]);
125     assert(message.address == "/abcde");
126 
127     message.parse([cast(ubyte)'/','a','b','c','d','e','f',0]);
128     assert(message.address == "/abcdef");
129     
130   }
131 }
132 
133 /**
134    oscLength includes the argument length and the type flag length
135  */
136 size_t oscLength(const(ubyte)[] s)
137 {
138   return ceil4(s.length + 1); // +1 for the terminating null
139 }
140 size_t oscLength(A...)(A args)
141 {
142   static if(A.length == 0) {
143     return 0;
144   } else {
145     size_t total = 0;
146 
147     foreach(arg; args) {
148       static if( isSomeString!(typeof(arg)) ) {
149 	total += oscLength(cast(const(ubyte)[])arg);
150       } else {
151 	total += oscLength(arg);
152 	//static assert(0, "oscLength function: unhandled arg type "~typeid(arg));
153       }
154     }
155 
156     // Add type flags ',' + A.length + '\0'
157     total += ceil4(2 + A.length);
158       
159     return total;
160   }
161 }
162 unittest
163 {
164   assert(oscLength("")    == 8);
165   assert(oscLength("a")   == 8);
166   assert(oscLength("ab")  == 8);
167   assert(oscLength("abc") == 8);
168   assert(oscLength("abcd")    == 12);
169   assert(oscLength("abcde")   == 12);
170   assert(oscLength("abcdef")  == 12);
171   assert(oscLength("abcdefg") == 12);
172   assert(oscLength("abcdefgh") == 16);
173 
174   assert(oscLength("","a","abcd") == 24);
175 }
176 
177 template oscTypeFlag(T)
178 {
179   static if( isSomeString!T ) {
180     enum oscTypeFlag = 's';
181   } else {
182     static assert(0, "oscTypeFlag: unhandled type "~typeid(T));
183   }
184 }
185 size_t oscSerialize(ubyte* buffer, const(ubyte)[] s)
186 {
187   buffer[0..s.length] = s[];
188   size_t limit = s.length + nullPaddingLength(s.length);
189   buffer[s.length..limit] = 0;
190   return limit;
191 }
192 //
193 // Todo: I could templatize the buffer type to support
194 //       using a buffer that might need to be resized
195 //
196 auto oscSerializeArgs(bool trackLength, A...)(ubyte* buffer, A args)
197 {
198   static if(trackLength) {
199     const ubyte* originalBuffer = buffer;
200   }
201 
202   static if(A.length > 0) {
203     //
204     // Serialize type flags
205     //
206     buffer[0] = ',';
207     foreach(i, arg; args) {
208       buffer[1+i] = oscTypeFlag!(typeof(arg));
209     }
210 
211     static if( nullPaddingLength(1 + A.length) == 1) {
212       buffer[1+A.length    ] = 0;
213       buffer += 1 + A.length + 1;
214     } else static if( nullPaddingLength(1 + A.length) == 2) {
215       buffer[1+A.length    ] = 0;
216       buffer[1+A.length + 1] = 0;
217       buffer += 1 + A.length + 2;
218     } else static if( nullPaddingLength(1 + A.length) == 3) {
219       buffer[1+A.length    ] = 0;
220       buffer[1+A.length + 1] = 0;
221       buffer[1+A.length + 2] = 0;
222       buffer += 1 + A.length + 3;
223     } else static if( nullPaddingLength(1 + A.length) == 4) {
224       buffer[1+A.length    ] = 0;
225       buffer[1+A.length + 1] = 0;
226       buffer[1+A.length + 2] = 0;
227       buffer[1+A.length + 3] = 0;
228       buffer += 1 + A.length + 4;
229     } else {
230       static assert(0, "bug");
231     }
232 
233     //
234     // Serialize arg values
235     //
236     foreach(arg; args) {
237       static if( isSomeString!(typeof(arg)) ) {
238 	buffer += oscSerialize(buffer, cast(const(ubyte)[])arg);
239       } else {
240 	//static assert(0, "oscSerialize function: unhandled arg type "~typeid(arg));
241 	buffer += oscSerialize(buffer, arg);
242       }
243     }
244 
245     static if(trackLength) {
246       return buffer - originalBuffer;
247     }
248   }
249 }
250 unittest
251 {
252   void test(A...)(ubyte[] expected, A args)
253   {
254     auto buffer = new ubyte[oscLength(args)];
255 
256     //assert(expected.length == buffer.length,
257     //format("expected %s bytes 
258 
259     oscSerializeArgs!false(buffer.ptr, args);
260     foreach(i, e; expected) {
261       if(e != buffer[i]) {
262 	writefln("Expected: %s", expected);
263 	writefln("Actual  : %s", buffer);
264 	writefln("Expected: %s", cast(string)expected);
265 	writefln("Actual  : %s", cast(string)buffer);
266 	assert(0);
267       }
268     }
269   }
270 
271   test(cast(ubyte[])",s\0\0abc\0", "abc");
272   test(cast(ubyte[])",s\0\0abcd\0\0\0\0", "abcd");
273   test(cast(ubyte[])",ss\0abc\0data\0\0\0\0", "abc", "data");
274 }
275 
276 
277 /**
278    Peel the top-most osc container/method from the osc address. The address is null terminated and must start with a forward slash.  address will point to the next forward slash or null. '/'.
279  */
280 inout(char)[] peel(ref inout(char)* address)
281 in { assert(*address == '/'); }
282 body
283 {
284   auto start = address + 1;
285   auto p = start;
286   while(true) {
287     auto c = *p;
288     if(c == 0 || c == '/') {
289       address = p;
290       return start[0..p-start];
291     }
292     p++;
293   }
294 }
295 unittest
296 {
297   immutable(char)* addr;
298 
299   addr = "/\0".ptr;
300   assert(peel(addr) == "");
301   assert(*addr == '\0');
302 
303   addr = "//".ptr;
304   assert(peel(addr) == "");
305   assert(*addr == '/');
306 
307   addr = "/a\0".ptr;
308   assert(peel(addr) == "a");
309   assert(*addr == '\0');
310 
311   addr = "/abcd\0".ptr;
312   assert(peel(addr) == "abcd");
313   assert(*addr == '\0');
314 
315   addr = "/abcd/".ptr;
316   assert(peel(addr) == "abcd");
317   assert(*addr == '/');
318 }
319 
320 
321 // Invalid characters for method/container
322 //    32 ' '
323 //    35 '#'
324 //    42 '*'
325 //    44 ','
326 //    47 '/'
327 //    63 '?'
328 //    91 '['
329 //    93 ']'
330 //   123 '{'
331 //   125 '}'
332 
333 /**
334    Implements a tree of Osc functions in a hiearchy.  Used to 
335    lookup functions via an OscAddress.
336  */
337 struct OscMethodTree
338 {
339   interface INode
340   {
341   }
342 
343   class MapNode : INode
344   {
345     INode[string] children;
346   }
347 
348   INode root;
349 
350   void dispatch(ref OscMessage message)
351   in { assert(message.address[0] == '/'); }
352   body
353   {
354     
355   }
356   
357 }
358 
359 
360 
361 
362 
363 
364 
365 
366 
367 
368 
369 /**
370    OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second.
371 */
372 struct TimeTag {
373   enum TimeTag immediate = TimeTag(1);
374   
375   ulong v;
376 /+
377   TimeTag() : v(1) {}
378   explicit TimeTag(uint64_t w): v(w) {}
379   operator uint64_t() const { return v; }
380   static TimeTag immediate() { return TimeTag(1); }+/
381 }
382 
383 /* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */
384 
385 
386 enum OscTypeFlag : char
387 {
388   true_   = 'T',
389   false_  = 'F', 
390   int32   = 'i',
391   int64   = 'h',
392   float_  = 'f',
393   double_ = 'd',
394   string_ = 's',
395   blob    = 'b',
396 }
397 
398 enum OscType {
399   true_,
400   false_, 
401   int32,
402   int64,
403   float_,
404   double_,
405   string_,
406   blob,
407 };
408 immutable char[] typeIDs = ['T','F','i','h','f','d','s','b'];
409 char getID(OscType type) 
410 {
411   return typeIDs[type];
412 }
413 unittest
414 {
415   foreach(type; OscType.min..OscType.max) {
416     //writefln("type '%s' id=%s", type, type.getID());
417     type.getID(); // make sure it works
418   }
419 }
420 
421 // round to the next multiple of 4, works for size_t and pointer arguments
422 size_t ceil4(size_t size)
423 {
424   return (size + 3) & ~(cast(size_t)3);
425 }
426 unittest
427 {
428   assert(ceil4(0) == 0);
429 
430   assert(ceil4(1) == 4);
431   assert(ceil4(2) == 4);
432   assert(ceil4(3) == 4);
433   assert(ceil4(4) == 4);
434 
435   assert(ceil4(5) == 8);
436   assert(ceil4(6) == 8);
437   assert(ceil4(7) == 8);
438   assert(ceil4(8) == 8);
439 }
440 
441 // returns 1-4
442 ubyte nullPaddingLength(size_t length) pure
443 {
444   final switch(length & 0x03) {
445   case 0: return 4;
446   case 1: return 3;
447   case 2: return 2;
448   case 3: return 1;
449   }
450 }
451 
452 
453 /+
454 // check that a memory area is zero padded until the next address which is a multiple of 4
455 inline bool isZeroPaddingCorrect(const char *p) {
456   const char *q = ceil4(p);
457   for (;p < q; ++p)
458     if (*p != 0) { return false; }
459   return true;
460 }
461 
462 +/
463 
464 union ValueUnion(T)
465 {
466   int[T.sizeof] data;
467   T value;
468 }
469 
470 /** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */
471 T oscDeserialize(T)(const(int)* data)
472 {
473   version(BigEndian) {
474     return *(cast(T*)data);
475   } else {
476     ValueUnion!T p;
477     foreach(i; 0..T.sizeof) {
478       p.data[i] = data[(T.sizeof-1) - i];
479     }
480     return p.value;
481   }
482 }
483 /** stored a POD type into an unaligned bytes array, using little endian representation */
484 void oscSerialize(T)(const T value, int* bytes)
485 {
486   version(BigEndian) {
487     bytes[0..T.sizeof] = (cast(int*)&value)[0..T.sizeof];
488   } else {
489     int* p = cast(int*)&value;
490     foreach(i; 0..T.sizeof) {
491       bytes[i] = p[(T.sizeof-1) - i];
492     }
493   }
494 }
495 
496 unittest
497 {
498   int[16] b;
499 
500   oscSerialize!int(3, b.ptr);
501   //writefln("%s", b);
502   assert(3 == oscDeserialize!int(b.ptr));
503 
504   b[0] = 3;
505   assert(3 == oscDeserialize!int(b.ptr));
506 }
507 
508 // see the OSC spec for the precise pattern matching rules
509 const(char)* internalPatternMatch(const(char) *pattern, const(char) *path) {
510   while (*pattern) {
511     const(char) *p = pattern;
512     if (*p == '?' && *path) { ++p; ++path; }
513     else if (*p == '[' && *path) { // bracketted range, e.g. [a-zABC]
514       ++p;
515       bool reverse = false;
516       if (*p == '!') {
517 	//debug writefln("[DEBUG] enter reversed bracket");
518 	reverse = true; ++p;
519       } else {
520 	//debug writefln("[DEBUG] enter bracket");
521       }
522       bool match = reverse;
523       for (; *p && *p != ']'; ++p) {
524         char c0 = *p, c1 = c0;
525         if (p[1] == '-' && p[2] && p[2] != ']') { p += 2; c1 = *p; }
526         if (*path >= c0 && *path <= c1) { match = !reverse; }
527       }
528       if (!match || *p != ']') return pattern;
529       ++p; ++path;
530     } else if (*p == '*') { // wildcard '*'
531       while (*p == '*') ++p; 
532       const(char)* best = null;
533       while (true) {
534         const char *ret = internalPatternMatch(p, path);
535         if (ret && ret > best) best = ret;
536         if (*path == 0 || *path == '/') break;
537         else ++path;
538       }
539       return best;
540     } else if (*p == '/' && *(p+1) == '/') { // the super-wildcard '//'
541       while (*(p+1)=='/') ++p;
542       const(char)* best = null;
543       while (true) {
544         const char *ret = internalPatternMatch(p, path);
545         if (ret && ret > best) best = ret;
546         if (*path == 0) break;
547         if (*path == 0 || (path = strchr(path+1, '/')) == null) break;
548       }      
549       return best;
550     } else if (*p == '{') { // braced list {foo,bar,baz}
551       const(char)* end = strchr(p, '}'), q;
552       if (!end) return null; // syntax error in brace list..
553       bool match = false;
554       do {
555         ++p;
556         q = strchr(p, ',');
557         if (q == null || q > end) q = end;
558         if (strncmp(p, path, q-p) == 0) {
559           path += (q-p); p = end+1; match = true;
560         } else p=q;
561       } while (q != end && !match);
562       if (!match) return pattern;
563     } else if (*p == *path) { ++p; ++path; } // any other character
564     else break;
565     pattern = p;
566   }
567   return (*path == 0 ? pattern : null);
568 }
569 
570 /** check if the path matches the supplied path pattern , according to the OSC spec pattern 
571     rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */
572 bool fullPatternMatch(const(char)[] pattern, const(char)[] test)
573 {
574   //debug writefln("[DEBUG] pattern '%s' match '%s'", pattern, test);
575   auto q = internalPatternMatch(pattern.ptr, test.ptr);
576   return q && *q == 0;
577 }
578 /** check if the path matches the beginning of pattern */
579 bool partialPatternMatch(const(char)[] pattern, const(char)[] test)
580 {
581   auto q = internalPatternMatch(pattern.ptr, test.ptr);
582   return q != null;
583 }
584 
585 unittest
586 {
587   char[16] buffer;
588 
589   assert(fullPatternMatch("", ""));
590   assert(fullPatternMatch("ab12", "ab12"));
591 
592   foreach(char c; 1..255) {
593     buffer[0] = c;
594     buffer[1] = '\0';
595     assert(fullPatternMatch("?", buffer));
596     buffer[0] = 'a';
597     buffer[1] = c;
598     buffer[2] = 'c';
599     buffer[3] = '\0';
600     assert(fullPatternMatch("a?c", buffer));
601   }
602 
603   // Test wildcard '*'
604   assert(fullPatternMatch("*", ""));
605   assert(fullPatternMatch("*", "1"));
606   assert(fullPatternMatch("*", "abcd"));
607   assert(fullPatternMatch("/*", "/info"));
608   assert(fullPatternMatch("*/", "info/"));
609   assert(fullPatternMatch("*/*", "info/specific"));
610 
611   // Test bracket []
612   assert(fullPatternMatch("[ab]", "a"));
613   assert(fullPatternMatch("[ab]", "b"));
614   // Test bracket [] with dash '-'
615   assert(fullPatternMatch("[-]", "-"));
616   assert(fullPatternMatch("[a-]", "a"));
617   assert(fullPatternMatch("[a-]", "-"));
618   assert(fullPatternMatch("[a-c]", "a"));
619   assert(fullPatternMatch("[a-c]", "b"));
620   assert(fullPatternMatch("[a-c]", "c"));
621   foreach(c; 'a'..'z') {
622     buffer[0] = c;
623     buffer[1] = '\0';
624     assert(fullPatternMatch("[a-z]", buffer));
625   }
626   // Test [!c]
627   buffer[1] = 0;
628   foreach(char not; 1..255) {
629     if(not == ']') continue;
630     auto pattern = "[!"~not~"]\0";
631     buffer[0] = not;
632     assert(!fullPatternMatch(pattern, buffer));
633 
634     foreach(char c; 1..255) {
635       if(c == not) continue;
636       buffer[0] = c;
637       assert(fullPatternMatch(pattern, buffer));
638     }
639   }
640   // Test [!min-max]
641   foreach(char c; 1..255) {
642     if(c >= 'a' && c <= 'z') continue;
643     buffer[0] = c;
644     buffer[1] = '\0';
645     assert(fullPatternMatch("[!a-z]", buffer));
646   }
647 
648   // Test brace expression
649   assert(fullPatternMatch("{abc}", "abc"));
650   assert(fullPatternMatch("{abc,123}", "abc"));
651   assert(fullPatternMatch("{abc,123}", "123"));
652 }
653 
654 /+
655 /** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */
656 struct Storage {
657   char[] buffer;
658   this(char[] buffer)
659   {
660     this.buffer = buffer;
661   }
662   char *getBytes(size_t sz) {
663     assert((data.size() & 3) == 0);
664     if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz)*2); }
665     size_t sz4 = ceil4(sz);
666     size_t pos = data.size(); 
667     data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK
668     return &(data[pos]);
669   }
670   char *begin() { return data.size() ? &data.front() : 0; }
671   char *end() { return begin() + size(); }
672   const char *begin() const { return data.size() ? &data.front() : 0; }
673   const char *end() const { return begin() + size(); }
674   size_t size() const { return data.size(); }
675   void assign(const char *beg, const char *end) { data.assign(beg, end); }
676   void clear() { data.resize(0); }
677 };
678 +/
679 /+
680 #if defined(OSCPKT_DEBUG)
681 #define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0)
682 #else
683 #define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0)
684 #endif
685 +/
686 enum ErrorCode {
687   ok,
688   // errors raised by the Message class:
689   malformedAddressPattern, malformedTypeTags, malformedArguments, unhandledTypeTags,
690   // errors raised by ArgReader
691   typeMismatch, notEnoughArgs, patternMismatch,
692   // errors raised by PacketReader/PacketWriter
693   invalidBundle, invalidPacketSize, bundleRequiredForMultiMessages,
694 }
695 
696 
697 struct ArgumentStorage
698 {
699   int[] int32Data;
700 }
701 
702 
703 
704 struct OscArg
705 {
706   size_t int32Offset;
707   size_t int32Length;
708 }
709 
710 /**
711    struct used to hold an OSC message that will be written or read.
712 
713    The list of arguments is exposed as a sort of queue. You "pop"
714    arguments from the front of the queue when reading, you push
715    arguments at the back of the queue when writing.
716 
717    Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")...
718 
719    Example of use:
720 
721    creation of a message:
722    @code
723    msg.init("/foo").pushInt32(4).pushStr("bar");
724    @endcode
725    reading a message, with error detection:
726    @code
727    if (msg.match("/foo/b*ar/plop")) {
728      int i; std::string s; std::vector<char> b;
729      if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) {
730        process message...;
731      } else arguments mismatch;
732    }
733    @endcode
734 */
735 class Message {
736   TimeTag timeTag;
737   string address;
738   string typeTags;
739   //OscArg[] arguments; // array of pairs (offset,length), offset being an index into the 'storage' array.
740   //Storage storage; // the arguments data is stored here
741   ArgumentStorage storage;
742   
743   ErrorCode error;
744 
745   /** ArgReader is used for popping arguments from a Message, holds a
746       pointer to the original Message, and maintains a local error code */
747   class ArgReader {
748     const Message *msg;
749     ErrorCode error;
750     size_t argIndex; // arg index of the next arg that will be popped out.
751     size_t argInt32Offset; // The offset of the current arg in the int32 buffer
752 
753     this(const Message* msg, ErrorCode error = ErrorCode.ok)
754     in { assert(msg != null); }
755     body
756     {
757       this.msg = msg;
758       this.error = (error != ErrorCode.ok) ? error : msg.error;
759     }
760 
761 /+
762     OscType currentTypeTag()
763     in { argIndex < msg.typeTags.length)
764     body {
765       return msg.typeTags[argIndex];
766     }
767 +/
768 
769     // returns 0 on error, otherwise the type char
770     char tryCurrentTypeChar() const
771     {
772       return (argIndex < msg.typeTags.length) ? msg.typeTags[argIndex] : 0;
773     }
774 
775     bool isBool() const { auto c = tryCurrentTypeChar(); return c == OscTypeFlag.true_ || c == OscTypeFlag.false_; }
776     bool isInt32() const { return tryCurrentTypeChar() == OscTypeFlag.int32; }
777     bool isInt64() const { return tryCurrentTypeChar() == OscTypeFlag.int64; }
778     bool isFloat() const { return tryCurrentTypeChar() == OscTypeFlag.float_; }
779     bool isDouble() const { return tryCurrentTypeChar() == OscTypeFlag.double_; }
780     bool isStr() const { return tryCurrentTypeChar() == OscTypeFlag.string_; }
781     bool isBlob() const { return tryCurrentTypeChar() == OscTypeFlag.blob; }
782 
783     size_t argsLeft() const { return msg.typeTags.length - argIndex; }
784     bool isOk() const { return error == ErrorCode.ok; }
785 
786     /** call this at the end of the popXXX() chain to make sure everything is ok and 
787         all arguments have been popped */
788     bool enforceDone() const { return error == ErrorCode.ok && argsLeft() == 0; }
789 
790     T pop(T)(OscTypeTag tag)
791     {
792       if(argIndex >= msg.typeTags.length)
793 	throw new Exception("There are no more arguments");
794       if(tag != tryCurrentTypeChar())
795 	throw new Exception(format("Expected type '%s' but current type is '%s'", tag, tryCurrentTypeChar()));
796       return oscDeserialize!T(msg.storage.bytes.ptr + msg.storage.int32Data[argInt32Offset]);
797     }
798 /+
799     /** retrieve an int32 argument */
800     int popInt32() { return popPod<int32_t>(OscTypeFlag.int32, i); }
801     /** retrieve an int64 argument */
802     ArgReader &popInt64(int64_t &i) { return popPod<int64_t>(OscTypeFlag.int64, i); }
803     /** retrieve a single precision floating point argument */
804     ArgReader &popFloat(float &f) { return popPod<float>(OscTypeFlag.float_, f); }
805     /** retrieve a double precision floating point argument */
806     ArgReader &popDouble(double &d) { return popPod<double>(OscTypeFlag.double_, d); }
807     /** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */
808     ArgReader &popStr(std::string &s) {
809       if (precheck(OscTypeFlag.string_)) {
810         s = argBeg(argIndex++);
811       }
812       return *this;
813     }
814     /** retrieve a binary blob */
815     ArgReader &popBlob(std::vector<char> &b) { 
816       if (precheck(OscTypeFlag.blob)) {
817         b.assign(argBeg(argIndex)+4, argEnd(argIndex)); 
818         ++argIndex;
819       }
820       return *this;
821     }
822     /** retrieve a boolean argument */
823     ArgReader &popBool(bool &b) {
824       b = false;
825       if (argIndex >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
826       else if (tryCurrentTypeChar() == OscTypeFlag.true_) b = true;
827       else if (tryCurrentTypeChar() == OscTypeFlag.false_) b = false;
828       else OSCPKT_SET_ERR(TYPE_MISMATCH);
829       ++argIndex;
830       return *this;
831     }
832     /** skip whatever comes next */
833     ArgReader &pop() {
834       if (argIndex >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
835       else ++argIndex;
836       return *this;
837     }
838   private:
839 +/
840 /+
841     auto argBeg(size_t argIndex) {
842       if (error || idx >= msg->arguments.size()) return 0; 
843       else return msg->storage.begin() + msg->arguments[idx].first;
844     }
845 +/
846 /+
847     const char *argEnd(size_t idx) {
848       if (error || idx >= msg->arguments.size()) return 0; 
849       else return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second;
850     }
851 +/
852 
853 /+
854     /* pre-check stuff before popping an argument from the message */
855     private bool precheck(int tag) { 
856       if (argIndex >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 
857       else if (!error && tryCurrentTypeChar() != tag) OSCPKT_SET_ERR(TYPE_MISMATCH);
858       return error == ErrorCode.ok;
859     }
860 +/
861   };
862 
863   //this() { clear(); }
864   this(string address, TimeTag timeTag = TimeTag.immediate)
865   {
866     this.address = address;
867     this.timeTag = timeTag;
868     this.error = ErrorCode.ok;
869   }
870 /+
871   this(const void *ptr, size_t sz, TimeTag timeTag = TimeTag.immediate)
872   {
873     buildFromRawData(ptr, sz); timeTag = tt;
874   }
875 +/
876   bool isOk() const { return error == ErrorCode.ok; }
877   ErrorCode getError() const { return error; }
878 
879   /** return the typeTags string, with its initial ',' stripped. */
880   //string typeTags() const { return typeTags; }
881 
882   /** retrieve the address pattern. If you want to follow to the whole OSC spec, you
883       have to handle its matching rules for address specifications -- this file does 
884       not provide this functionality */
885   //string addressPattern() const { return address; }
886   //TimeTag timeTag() const { return timeTag; }
887 
888   /** reset the message to a clean state */
889   void clear()
890   {
891     address = null;
892     typeTags = null;
893     //storage.clear();
894     //arguments = null;
895     error = ErrorCode.ok;
896     timeTag = TimeTag.immediate;
897   }
898 
899   /** clear the message and start a new message with the supplied address and timeTag. */
900   void init(string address, TimeTag timeTag = TimeTag.immediate)
901   in { assert(address.length > 0 && address[0] == '/'); }
902   body {
903     clear();
904     this.address = address;
905     this.timeTag = timeTag;
906   }
907 
908 /+
909   /** start a matching test. The typical use-case is to follow this by
910       a sequence of calls to popXXX() and a final call to
911       enforceDone() which will allow to check that everything went
912       fine. For example:
913       @code
914       if (msg.match("/foo").popInt32(i).enforceDone()) { blah(i); } 
915       else if (msg.match("/bar").popStr(s).popInt32(i).enforceDone()) { plop(s,i); }
916       else cerr << "unhandled message: " << msg << "\n";
917       @endcode
918   */
919   ArgReader match(const std::string &test) const {
920     return ArgReader(*this, fullPatternMatch(address.c_str(), test.c_str()) ? ErrorCode.ok : PATTERN_MISMATCH);
921   }
922   /** return true if the 'test' path matched by the first characters of addressPattern().
923       For ex. ("/foo/bar").partialMatch("/foo/") is true */
924   ArgReader partialMatch(const std::string &test) const {
925     return ArgReader(*this, partialPatternMatch(address.c_str(), test.c_str()) ? ErrorCode.ok : PATTERN_MISMATCH);
926   }
927   ArgReader arg() const { return ArgReader(*this, ErrorCode.ok); }
928 
929   /** build the osc message for raw data (the message will keep a copy of that data) */
930   void buildFromRawData(const void *ptr, size_t sz) {
931     clear();
932     storage.assign((const char*)ptr, (const char*)ptr + sz);
933     const char *address_beg = storage.begin();
934     const char *address_end = (const char*)memchr(address_beg, 0, storage.end()-address_beg);
935     if (!address_end || !isZeroPaddingCorrect(address_end+1) || address_beg[0] != '/') { 
936       OSCPKT_SET_ERROR(MALFORMED_ADDRESS_PATTERN); return; 
937     } else address.assign(address_beg, address_end);
938 
939     const char *typeTags_beg = ceil4(address_end+1);
940     const char *typeTags_end = (const char*)memchr(typeTags_beg, 0, storage.end()-typeTags_beg);
941     if (!typeTags_end || !isZeroPaddingCorrect(typeTags_end+1) || typeTags_beg[0] != ',') { 
942       OSCPKT_SET_ERROR(MALFORMED_TYPETAGS); return; 
943     } else typeTags.assign(typeTags_beg+1, typeTags_end); // we do not copy the initial ','
944 
945     const char *arg = ceil4(typeTags_end+1); assert(arg <= storage.end()); 
946     size_t iarg = 0;
947     while (isOk() && iarg < typeTags.size()) {
948       assert(arg <= storage.end()); 
949       size_t len = getArgSize(typeTags[iarg], arg);
950       if (isOk()) arguments.push_back(std::make_pair(arg - storage.begin(), len));
951       arg += ceil4(len); ++iarg;
952     }
953     if (iarg < typeTags.size() || arg != storage.end()) {
954       OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
955     }
956   }
957 
958   /* below are all the functions that serve when *writing* a message */
959   Message &pushBool(bool b) { 
960     typeTags += (b ? OscTypeFlag.true_ : OscTypeFlag.false_); 
961     arguments.push_back(std::make_pair(storage.size(), storage.size()));
962     return *this;
963   }
964   Message &pushInt32(int32_t i) { return pushPod(OscTypeFlag.int32, i); }
965   Message &pushInt64(int64_t h) { return pushPod(OscTypeFlag.int64, h); }
966   Message &pushFloat(float f) { return pushPod(OscTypeFlag.float_, f); }
967   Message &pushDouble(double d) { return pushPod(OscTypeFlag.double_, d); }
968   Message &pushStr(const std::string &s) {
969     assert(s.size() < 2147483647); // insane values are not welcome
970     typeTags += OscTypeFlag.string_;
971     arguments.push_back(std::make_pair(storage.size(), s.size() + 1));
972     strcpy(storage.getBytes(s.size()+1), s.c_str());
973     return *this;
974   }
975   Message &pushBlob(void *ptr, size_t num_bytes) {
976     assert(num_bytes < 2147483647); // insane values are not welcome
977     typeTags += OscTypeFlag.blob; 
978     arguments.push_back(std::make_pair(storage.size(), num_bytes+4));
979     oscSerialize<int32_t>((int32_t)num_bytes, storage.getBytes(4));
980     if (num_bytes)
981       memcpy(storage.getBytes(num_bytes), ptr, num_bytes);
982     return *this;
983   }
984 
985 
986   /** write the raw message data (used by PacketWriter) */
987   void packMessage(Storage &s, bool write_size) const {
988     if (!isOk()) return;
989     size_t l_addr = address.size()+1, l_type = typeTags.size()+2;
990     if (write_size) 
991       oscSerialize<uint32_t>(uint32_t(ceil4(l_addr) + ceil4(l_type) + ceil4(storage.size())), s.getBytes(4));
992     strcpy(s.getBytes(l_addr), address.c_str());
993     strcpy(s.getBytes(l_type), ("," + typeTags).c_str());
994     if (storage.size())
995       memcpy(s.getBytes(storage.size()), const_cast<Storage&>(storage).begin(), storage.size());
996   }
997 
998 private:
999 
1000   /* get the number of bytes occupied by the argument */
1001   size_t getArgSize(int type, const char *p) {
1002     if (error) return 0;
1003     size_t sz = 0;
1004     assert(p >= storage.begin() && p <= storage.end());
1005     switch (type) {
1006       case OscTypeFlag.true_:
1007       case OscTypeFlag.false_: sz = 0; break;
1008       case OscTypeFlag.int32: 
1009       case OscTypeFlag.float_: sz = 4; break;
1010       case OscTypeFlag.int64: 
1011       case OscTypeFlag.double_: sz = 8; break;
1012       case OscTypeFlag.string_: {
1013         const char *q = (const char*)memchr(p, 0, storage.end()-p);
1014         if (!q) OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
1015         else sz = (q-p)+1;
1016       } break;
1017       case OscTypeFlag.blob: {
1018         if (p == storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
1019         sz = 4+oscDeserialize<uint32_t>(p);
1020       } break;
1021       default: {
1022         OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS); return 0;
1023       } break;
1024     }
1025     if (p+sz > storage.end() || /* string or blob too large.. */
1026         p+sz < p /* or even blob so large that it did overflow */) { 
1027       OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; 
1028     }
1029     if (!isZeroPaddingCorrect(p+sz)) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; }
1030     return sz;
1031   }
1032 
1033   template <typename POD> Message &pushPod(int tag, POD v) {
1034     typeTags += (char)tag; 
1035     arguments.push_back(std::make_pair(storage.size(), sizeof(POD)));
1036     oscSerialize(v, storage.getBytes(sizeof(POD))); 
1037     return *this;
1038   }
1039 
1040 #ifdef OSCPKT_OSTREAM_OUTPUT
1041   friend std::ostream &operator<<(std::ostream &os, const Message &msg) {
1042     os << "osc_address: '" << msg.address << "', types: '" << msg.typeTags << "', timetag=" << msg.timeTag << ", args=[";
1043     Message::ArgReader arg(msg);
1044     while (arg.argsLeft() && arg.isOk()) {
1045       if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); }
1046       else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; }
1047       else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; }
1048       else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; }
1049       else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; }
1050       else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; }
1051       else if (arg.isBlob()) { std::vector<char> b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; }
1052       else {
1053         assert(0); // I forgot a case..
1054       }
1055       if (arg.argsLeft()) os << ", ";
1056     }
1057     if (!arg.isOk()) { os << " ERROR#" << arg.getError(); }
1058     os << "]";
1059     return os;
1060   }
1061 #endif
1062 +/
1063 
1064 };
1065 /+
1066 /**
1067    parse an OSC packet and extracts the embedded OSC messages. 
1068 */
1069 class PacketReader {
1070 public:
1071   PacketReader() { error = ErrorCode.ok; }
1072   /** pointer and size of the osc packet to be parsed. */
1073   PacketReader(const void *ptr, size_t sz) { init(ptr, sz); }
1074 
1075   void init(const void *ptr, size_t sz) {
1076     error = ErrorCode.ok; messages.clear();
1077     if ((sz%4) == 0) { 
1078       parse((const char*)ptr, (const char *)ptr+sz, TimeTag::immediate());
1079     } else OSCPKT_SET_ERR(INVALID_PACKET_SIZE);
1080     it_messages = messages.begin();
1081   }
1082   
1083   /** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */
1084   Message *popMessage() {
1085     if (!error && !messages.empty() && it_messages != messages.end()) return &*it_messages++;
1086     else return 0;
1087   }
1088   bool isOk() const { return error == ErrorCode.ok; }
1089   ErrorCode getError() const { return error; }
1090 
1091 private:
1092   std::list<Message> messages;
1093   std::list<Message>::iterator it_messages;
1094   ErrorCode error;
1095   
1096   void parse(const char *beg, const char *end, TimeTag timeTag) {
1097     assert(beg <= end && !error); assert(((end-beg)%4)==0);
1098     
1099     if (beg == end) return;
1100     if (*beg == '#') {
1101       /* it's a bundle */
1102       if (end - beg >= 20 
1103           && memcmp(beg, "#bundle\0", 8) == 0) {
1104         TimeTag timeTag2(oscDeserialize<uint64_t>(beg+8));
1105         const char *pos = beg + 16;
1106         do {
1107           uint32_t sz = oscDeserialize<uint32_t>(pos); pos += 4;
1108           if ((sz&3) != 0 || pos + sz > end || pos+sz < pos) {
1109             OSCPKT_SET_ERR(INVALID_BUNDLE);
1110           } else {
1111             parse(pos, pos+sz, timeTag2);
1112             pos += sz;
1113           }
1114         } while (!error && pos != end);
1115       } else {
1116         OSCPKT_SET_ERR(INVALID_BUNDLE);
1117       }
1118     } else {
1119       messages.push_back(Message(beg, end-beg, timeTag));
1120       if (!messages.back().isOk()) OSCPKT_SET_ERR(messages.back().getError());
1121     }
1122   }
1123 };
1124 
1125 
1126 /**
1127    Assemble messages into an OSC packet. Example of use:
1128    @code
1129    PacketWriter pkt; 
1130    Message msg;
1131    pkt.startBundle(); 
1132    pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f));
1133    pkt.addMessage(msg.init("/bar").pushBool(false));
1134    pkt.endBundle();
1135    if (pkt.isOk()) {
1136      send(pkt.data(), pkt.size());
1137    }
1138    @endcode
1139 */
1140 class PacketWriter {
1141 public:
1142   PacketWriter() { init(); }
1143   PacketWriter &init() { err = ErrorCode.ok; storage.clear(); bundles.clear(); return *this; }
1144   
1145   /** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to 
1146       put them in a bundle. Nested bundles inside bundles are also allowed. */
1147   PacketWriter &startBundle(TimeTag ts = TimeTag::immediate()) {
1148     char *p;
1149     if (bundles.size()) p = storage.getBytes(4); // hold the bundle size
1150     p = storage.getBytes(8); strcpy(p, "#bundle"); bundles.push_back(p - storage.begin());
1151     p = storage.getBytes(8); oscSerialize<uint64_t>(ts, p);
1152     return *this;
1153   }
1154   /** close the current bundle. */
1155   PacketWriter &endBundle() {
1156     if (bundles.size()) {
1157       if (storage.size() - bundles.back() == 16) {
1158         oscSerialize<uint32_t>(0, storage.getBytes(4)); // the 'empty bundle' case, not very elegant
1159       }
1160       if (bundles.size()>1) { // no size stored for the top-level bundle
1161         oscSerialize<uint32_t>(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back()-4);
1162       }
1163       bundles.pop_back();      
1164     } else OSCPKT_SET_ERR(INVALID_BUNDLE);
1165     return *this;
1166   }
1167 
1168   /** insert an Osc message into the current bundle / packet.
1169    */
1170   PacketWriter &addMessage(const Message &msg) {
1171     if (storage.size() != 0 && bundles.empty()) OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES);
1172     else msg.packMessage(storage, bundles.size()>0);
1173     if (!msg.isOk()) OSCPKT_SET_ERR(msg.getError());
1174     return *this;
1175   }
1176 
1177   /** the error flag will be raised if an opened bundle is not closed, or if more than one message is
1178       inserted in the packet without a bundle */
1179   bool isOk() { return err == ErrorCode.ok; }
1180   ErrorCode getError() { return err; }
1181 
1182   /** return the number of bytes of the osc packet -- will always be a
1183       multiple of 4 -- returns 0 if the construction of the packet has
1184       failed. */
1185   uint32_t packetSize() { return err ? 0 : (uint32_t)storage.size(); }
1186   
1187   /** return the bytes of the osc packet (NULL if the construction of the packet has failed) */
1188   char *packetData() { return err ? 0 : storage.begin(); }
1189 private:  
1190   std::vector<size_t> bundles; // hold the position in the storage array of the beginning marker of each bundle
1191   Storage storage;
1192   ErrorCode err;
1193 };
1194 
1195 
1196 } // namespace oscpkt
1197 
1198 #endif // OSCPKT_HH
1199 +/