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 +/