fast_osc 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,781 @@
1
+ #include <stdint.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <stdarg.h>
5
+ #include <stdbool.h>
6
+ #include <ctype.h>
7
+ #include <assert.h>
8
+
9
+ // #include <rtosc.h>
10
+
11
+ const char *rtosc_argument_string(const char *msg)
12
+ {
13
+ assert(msg && *msg);
14
+ while(*++msg); //skip pattern
15
+ while(!*++msg);//skip null
16
+ return msg+1; //skip comma
17
+ }
18
+
19
+ unsigned rtosc_narguments(const char *msg)
20
+ {
21
+ const char *args = rtosc_argument_string(msg);
22
+ int nargs = 0;
23
+ while(*args++)
24
+ nargs += (*args == ']' || *args == '[') ? 0 : 1;
25
+ return nargs;
26
+ }
27
+
28
+ static int has_reserved(char type)
29
+ {
30
+ switch(type)
31
+ {
32
+ case 'i'://official types
33
+ case 's':
34
+ case 'b':
35
+ case 'f':
36
+
37
+ case 'h'://unofficial
38
+ case 't':
39
+ case 'd':
40
+ case 'S':
41
+ case 'r':
42
+ case 'm':
43
+ case 'c':
44
+ return 1;
45
+ case 'T':
46
+ case 'F':
47
+ case 'N':
48
+ case 'I':
49
+ case '[':
50
+ case ']':
51
+ return 0;
52
+ }
53
+
54
+ //Should not happen
55
+ return 0;
56
+ }
57
+
58
+ static unsigned nreserved(const char *args)
59
+ {
60
+ unsigned res = 0;
61
+ for(;*args;++args)
62
+ res += has_reserved(*args);
63
+
64
+ return res;
65
+ }
66
+
67
+ char rtosc_type(const char *msg, unsigned nargument)
68
+ {
69
+ assert(nargument < rtosc_narguments(msg));
70
+ const char *arg = rtosc_argument_string(msg);
71
+ while(1) {
72
+ if(*arg == '[' || *arg == ']')
73
+ ++arg;
74
+ else if(!nargument || !*arg)
75
+ return *arg;
76
+ else
77
+ ++arg, --nargument;
78
+ }
79
+ }
80
+
81
+ static unsigned arg_start(const char *msg_)
82
+ {
83
+ const uint8_t *msg = (const uint8_t*)msg_;
84
+ //Iterate to the right position
85
+ const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg_);
86
+ const uint8_t *aligned_ptr = args-1;
87
+ const uint8_t *arg_pos = args;
88
+
89
+ while(*++arg_pos);
90
+ //Alignment
91
+ arg_pos += 4-(arg_pos-aligned_ptr)%4;
92
+ return arg_pos-msg;
93
+ }
94
+
95
+ static unsigned arg_size(const uint8_t *arg_mem, char type)
96
+ {
97
+ if(!has_reserved(type))
98
+ return 0;
99
+ const uint8_t *arg_pos=arg_mem;
100
+ uint32_t blob_length = 0;
101
+ switch(type)
102
+ {
103
+ case 'h':
104
+ case 't':
105
+ case 'd':
106
+ return 8;
107
+ case 'm':
108
+ case 'r':
109
+ case 'f':
110
+ case 'c':
111
+ case 'i':
112
+ return 4;
113
+ case 'S':
114
+ case 's':
115
+ while(*++arg_pos);
116
+ arg_pos += 4-(arg_pos-arg_mem)%4;
117
+ return arg_pos-arg_mem;
118
+ case 'b':
119
+ blob_length |= (*arg_pos++ << 24);
120
+ blob_length |= (*arg_pos++ << 16);
121
+ blob_length |= (*arg_pos++ << 8);
122
+ blob_length |= (*arg_pos++);
123
+ if(blob_length%4)
124
+ blob_length += 4-blob_length%4;
125
+ arg_pos += blob_length;
126
+ return arg_pos-arg_mem;
127
+ default:
128
+ assert("Invalid Type");
129
+ }
130
+ return -1;
131
+ }
132
+
133
+ static unsigned arg_off(const char *msg, unsigned idx)
134
+ {
135
+ if(!has_reserved(rtosc_type(msg,idx)))
136
+ return 0;
137
+
138
+ //Iterate to the right position
139
+ const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg);
140
+ const uint8_t *aligned_ptr = args-1;
141
+ const uint8_t *arg_pos = args;
142
+
143
+ while(*++arg_pos);
144
+ //Alignment
145
+ arg_pos += 4-(arg_pos-((uint8_t*)aligned_ptr))%4;
146
+
147
+ //ignore any leading '[' or ']'
148
+ while(*args == '[' || *args == ']')
149
+ ++args;
150
+
151
+ while(idx--) {
152
+ char type = *args++;
153
+ if(type == '[' || type == ']')
154
+ idx++;//not a valid arg idx
155
+ else
156
+ arg_pos += arg_size(arg_pos, type);
157
+ }
158
+ return arg_pos-(uint8_t*)msg;
159
+ }
160
+
161
+ size_t rtosc_message(char *buffer,
162
+ size_t len,
163
+ const char *address,
164
+ const char *arguments,
165
+ ...)
166
+ {
167
+ va_list va;
168
+ va_start(va, arguments);
169
+ size_t result = rtosc_vmessage(buffer, len, address, arguments, va);
170
+ va_end(va);
171
+ return result;
172
+ }
173
+
174
+ //Calculate the size of the message without writing to a buffer
175
+ static size_t vsosc_null(const char *address,
176
+ const char *arguments,
177
+ const rtosc_arg_t *args)
178
+ {
179
+ unsigned pos = 0;
180
+ pos += strlen(address);
181
+ pos += 4-pos%4;//get 32 bit alignment
182
+ pos += 1+strlen(arguments);
183
+ pos += 4-pos%4;
184
+
185
+ unsigned toparse = nreserved(arguments);
186
+ unsigned arg_pos = 0;
187
+
188
+ //Take care of varargs
189
+ while(toparse)
190
+ {
191
+ char arg = *arguments++;
192
+ assert(arg);
193
+ int i;
194
+ const char *s;
195
+ switch(arg) {
196
+ case 'h':
197
+ case 't':
198
+ case 'd':
199
+ ++arg_pos;
200
+ pos += 8;
201
+ --toparse;
202
+ break;
203
+ case 'm':
204
+ case 'r':
205
+ case 'c':
206
+ case 'f':
207
+ case 'i':
208
+ ++arg_pos;
209
+ pos += 4;
210
+ --toparse;
211
+ break;
212
+ case 's':
213
+ case 'S':
214
+ s = args[arg_pos++].s;
215
+ assert(s && "Input strings CANNOT be NULL");
216
+ pos += strlen(s);
217
+ pos += 4-pos%4;
218
+ --toparse;
219
+ break;
220
+ case 'b':
221
+ i = args[arg_pos++].b.len;
222
+ pos += 4 + i;
223
+ if(pos%4)
224
+ pos += 4-pos%4;
225
+ --toparse;
226
+ break;
227
+ default:
228
+ ;
229
+ }
230
+ }
231
+
232
+ return pos;
233
+ }
234
+ size_t rtosc_vmessage(char *buffer,
235
+ size_t len,
236
+ const char *address,
237
+ const char *arguments,
238
+ va_list ap)
239
+ {
240
+ const unsigned nargs = nreserved(arguments);
241
+ if(!nargs)
242
+ return rtosc_amessage(buffer,len,address,arguments,NULL);
243
+
244
+ rtosc_arg_t args[nargs];
245
+
246
+ unsigned arg_pos = 0;
247
+ const char *arg_str = arguments;
248
+ uint8_t *midi_tmp;
249
+ while(arg_pos < nargs)
250
+ {
251
+ switch(*arg_str++) {
252
+ case 'h':
253
+ case 't':
254
+ args[arg_pos++].h = va_arg(ap, int64_t);
255
+ break;
256
+ case 'd':
257
+ args[arg_pos++].d = va_arg(ap, double);
258
+ break;
259
+ case 'c':
260
+ case 'i':
261
+ case 'r':
262
+ args[arg_pos++].i = va_arg(ap, int);
263
+ break;
264
+ case 'm':
265
+ midi_tmp = va_arg(ap, uint8_t *);
266
+ args[arg_pos].m[0] = midi_tmp[0];
267
+ args[arg_pos].m[1] = midi_tmp[1];
268
+ args[arg_pos].m[2] = midi_tmp[2];
269
+ args[arg_pos++].m[3] = midi_tmp[3];
270
+ break;
271
+ case 'S':
272
+ case 's':
273
+ args[arg_pos++].s = va_arg(ap, const char *);
274
+ break;
275
+ case 'b':
276
+ args[arg_pos].b.len = va_arg(ap, int);
277
+ args[arg_pos].b.data = va_arg(ap, unsigned char *);
278
+ arg_pos++;
279
+ break;
280
+ case 'f':
281
+ args[arg_pos++].f = va_arg(ap, double);
282
+ break;
283
+ default:
284
+ ;
285
+ }
286
+ }
287
+
288
+ return rtosc_amessage(buffer,len,address,arguments,args);
289
+ }
290
+
291
+ size_t rtosc_amessage(char *buffer,
292
+ size_t len,
293
+ const char *address,
294
+ const char *arguments,
295
+ const rtosc_arg_t *args)
296
+ {
297
+ const size_t total_len = vsosc_null(address, arguments, args);
298
+
299
+ if(!buffer)
300
+ return total_len;
301
+
302
+ //Abort if the message cannot fit
303
+ if(total_len>len) {
304
+ memset(buffer, 0, len);
305
+ return 0;
306
+ }
307
+
308
+ memset(buffer, 0, total_len);
309
+
310
+ unsigned pos = 0;
311
+ while(*address)
312
+ buffer[pos++] = *address++;
313
+
314
+ //get 32 bit alignment
315
+ pos += 4-pos%4;
316
+
317
+ buffer[pos++] = ',';
318
+
319
+ const char *arg_str = arguments;
320
+ while(*arg_str)
321
+ buffer[pos++] = *arg_str++;
322
+
323
+ pos += 4-pos%4;
324
+
325
+ unsigned toparse = nreserved(arguments);
326
+ unsigned arg_pos = 0;
327
+ while(toparse)
328
+ {
329
+ char arg = *arguments++;
330
+ assert(arg);
331
+ int32_t i;
332
+ int64_t d;
333
+ const uint8_t *m;
334
+ const char *s;
335
+ const unsigned char *u;
336
+ rtosc_blob_t b;
337
+ switch(arg) {
338
+ case 'h':
339
+ case 't':
340
+ case 'd':
341
+ d = args[arg_pos++].t;
342
+ buffer[pos++] = ((d>>56) & 0xff);
343
+ buffer[pos++] = ((d>>48) & 0xff);
344
+ buffer[pos++] = ((d>>40) & 0xff);
345
+ buffer[pos++] = ((d>>32) & 0xff);
346
+ buffer[pos++] = ((d>>24) & 0xff);
347
+ buffer[pos++] = ((d>>16) & 0xff);
348
+ buffer[pos++] = ((d>>8) & 0xff);
349
+ buffer[pos++] = (d & 0xff);
350
+ --toparse;
351
+ break;
352
+ case 'r':
353
+ case 'f':
354
+ case 'c':
355
+ case 'i':
356
+ i = args[arg_pos++].i;
357
+ buffer[pos++] = ((i>>24) & 0xff);
358
+ buffer[pos++] = ((i>>16) & 0xff);
359
+ buffer[pos++] = ((i>>8) & 0xff);
360
+ buffer[pos++] = (i & 0xff);
361
+ --toparse;
362
+ break;
363
+ case 'm':
364
+ //TODO verify ordering of spec
365
+ m = args[arg_pos++].m;
366
+ buffer[pos++] = m[0];
367
+ buffer[pos++] = m[1];
368
+ buffer[pos++] = m[2];
369
+ buffer[pos++] = m[3];
370
+ --toparse;
371
+ break;
372
+ case 'S':
373
+ case 's':
374
+ s = args[arg_pos++].s;
375
+ while(*s)
376
+ buffer[pos++] = *s++;
377
+ pos += 4-pos%4;
378
+ --toparse;
379
+ break;
380
+ case 'b':
381
+ b = args[arg_pos++].b;
382
+ i = b.len;
383
+ buffer[pos++] = ((i>>24) & 0xff);
384
+ buffer[pos++] = ((i>>16) & 0xff);
385
+ buffer[pos++] = ((i>>8) & 0xff);
386
+ buffer[pos++] = (i & 0xff);
387
+ u = b.data;
388
+ if(u) {
389
+ while(i--)
390
+ buffer[pos++] = *u++;
391
+ }
392
+ else
393
+ pos += i;
394
+ if(pos%4)
395
+ pos += 4-pos%4;
396
+ --toparse;
397
+ break;
398
+ default:
399
+ ;
400
+ }
401
+ }
402
+
403
+ return pos;
404
+ }
405
+
406
+ static rtosc_arg_t extract_arg(const uint8_t *arg_pos, char type)
407
+ {
408
+ rtosc_arg_t result = {0};
409
+ //trivial case
410
+ if(!has_reserved(type)) {
411
+ switch(type)
412
+ {
413
+ case 'T':
414
+ result.T = true;
415
+ break;
416
+ case 'F':
417
+ result.T = false;
418
+ break;
419
+ default:
420
+ ;
421
+ }
422
+ } else {
423
+ switch(type)
424
+ {
425
+ case 'h':
426
+ case 't':
427
+ case 'd':
428
+ result.t |= (((uint64_t)*arg_pos++) << 56);
429
+ result.t |= (((uint64_t)*arg_pos++) << 48);
430
+ result.t |= (((uint64_t)*arg_pos++) << 40);
431
+ result.t |= (((uint64_t)*arg_pos++) << 32);
432
+ result.t |= (((uint64_t)*arg_pos++) << 24);
433
+ result.t |= (((uint64_t)*arg_pos++) << 16);
434
+ result.t |= (((uint64_t)*arg_pos++) << 8);
435
+ result.t |= (((uint64_t)*arg_pos++));
436
+ break;
437
+ case 'r':
438
+ case 'f':
439
+ case 'c':
440
+ case 'i':
441
+ result.i |= (*arg_pos++ << 24);
442
+ result.i |= (*arg_pos++ << 16);
443
+ result.i |= (*arg_pos++ << 8);
444
+ result.i |= (*arg_pos++);
445
+ break;
446
+ case 'm':
447
+ result.m[0] = *arg_pos++;
448
+ result.m[1] = *arg_pos++;
449
+ result.m[2] = *arg_pos++;
450
+ result.m[3] = *arg_pos++;
451
+ break;
452
+ case 'b':
453
+ result.b.len |= (*arg_pos++ << 24);
454
+ result.b.len |= (*arg_pos++ << 16);
455
+ result.b.len |= (*arg_pos++ << 8);
456
+ result.b.len |= (*arg_pos++);
457
+ result.b.data = (unsigned char *)arg_pos;
458
+ break;
459
+ case 'S':
460
+ case 's':
461
+ result.s = (char *)arg_pos;
462
+ break;
463
+ }
464
+ }
465
+
466
+ return result;
467
+ }
468
+
469
+ static const char *advance_past_dummy_args(const char *args)
470
+ {
471
+ while(*args == '[' || *args == ']')
472
+ args++;
473
+ return args;
474
+ }
475
+
476
+ rtosc_arg_itr_t rtosc_itr_begin(const char *msg)
477
+ {
478
+ rtosc_arg_itr_t itr;
479
+ itr.type_pos = advance_past_dummy_args(rtosc_argument_string(msg));
480
+ itr.value_pos = (uint8_t*)(msg+arg_start(msg));
481
+
482
+ return itr;
483
+ }
484
+
485
+ rtosc_arg_val_t rtosc_itr_next(rtosc_arg_itr_t *itr)
486
+ {
487
+ //current position provides the value
488
+ rtosc_arg_val_t result = {0,{0}};
489
+ result.type = *itr->type_pos;
490
+ if(result.type)
491
+ result.val = extract_arg(itr->value_pos, result.type);
492
+
493
+ //advance
494
+ itr->type_pos = advance_past_dummy_args(itr->type_pos+1);
495
+ char type = result.type;
496
+ int size = arg_size(itr->value_pos, type);
497
+ itr->value_pos += size;
498
+
499
+
500
+ return result;
501
+ }
502
+
503
+ int rtosc_itr_end(rtosc_arg_itr_t itr)
504
+ {
505
+ return !itr.type_pos || !*itr.type_pos;
506
+ }
507
+
508
+ rtosc_arg_t rtosc_argument(const char *msg, unsigned idx)
509
+ {
510
+ char type = rtosc_type(msg, idx);
511
+ uint8_t *arg_mem = (uint8_t*)msg + arg_off(msg, idx);
512
+ return extract_arg(arg_mem, type);
513
+ }
514
+
515
+ static unsigned char deref(unsigned pos, ring_t *ring)
516
+ {
517
+ return pos<ring[0].len ? ring[0].data[pos] :
518
+ ((pos-ring[0].len)<ring[1].len ? ring[1].data[pos-ring[0].len] : 0x00);
519
+ }
520
+
521
+ static size_t bundle_ring_length(ring_t *ring)
522
+ {
523
+ unsigned pos = 8+8;//goto first length field
524
+ uint32_t advance = 0;
525
+ do {
526
+ advance = deref(pos+0, ring) << (8*3) |
527
+ deref(pos+1, ring) << (8*2) |
528
+ deref(pos+2, ring) << (8*1) |
529
+ deref(pos+3, ring) << (8*0);
530
+ if(advance)
531
+ pos += 4+advance;
532
+ } while(advance);
533
+
534
+ return pos <= (ring[0].len+ring[1].len) ? pos : 0;
535
+ }
536
+
537
+ //Zero means no full message present
538
+ size_t rtosc_message_ring_length(ring_t *ring)
539
+ {
540
+ //Check if the message is a bundle
541
+ if(deref(0,ring) == '#' &&
542
+ deref(1,ring) == 'b' &&
543
+ deref(2,ring) == 'u' &&
544
+ deref(3,ring) == 'n' &&
545
+ deref(4,ring) == 'd' &&
546
+ deref(5,ring) == 'l' &&
547
+ deref(6,ring) == 'e' &&
548
+ deref(7,ring) == '\0')
549
+ return bundle_ring_length(ring);
550
+
551
+ //Proceed for normal messages
552
+ //Consume path
553
+ unsigned pos = 0;
554
+ while(deref(pos++,ring));
555
+ pos--;
556
+
557
+ //Travel through the null word end [1..4] bytes
558
+ for(int i=0; i<4; ++i)
559
+ if(deref(++pos, ring))
560
+ break;
561
+
562
+ if(deref(pos, ring) != ',')
563
+ return 0;
564
+
565
+ unsigned aligned_pos = pos;
566
+ int arguments = pos+1;
567
+ while(deref(++pos,ring));
568
+ pos += 4-(pos-aligned_pos)%4;
569
+
570
+ unsigned toparse = 0;
571
+ {
572
+ int arg = arguments-1;
573
+ while(deref(++arg,ring))
574
+ toparse += has_reserved(deref(arg,ring));
575
+ }
576
+
577
+ //Take care of varargs
578
+ while(toparse)
579
+ {
580
+ char arg = deref(arguments++,ring);
581
+ assert(arg);
582
+ uint32_t i;
583
+ switch(arg) {
584
+ case 'h':
585
+ case 't':
586
+ case 'd':
587
+ pos += 8;
588
+ --toparse;
589
+ break;
590
+ case 'm':
591
+ case 'r':
592
+ case 'c':
593
+ case 'f':
594
+ case 'i':
595
+ pos += 4;
596
+ --toparse;
597
+ break;
598
+ case 'S':
599
+ case 's':
600
+ while(deref(++pos,ring));
601
+ pos += 4-(pos-aligned_pos)%4;
602
+ --toparse;
603
+ break;
604
+ case 'b':
605
+ i = 0;
606
+ i |= (deref(pos++,ring) << 24);
607
+ i |= (deref(pos++,ring) << 16);
608
+ i |= (deref(pos++,ring) << 8);
609
+ i |= (deref(pos++,ring));
610
+ pos += i;
611
+ if((pos-aligned_pos)%4)
612
+ pos += 4-(pos-aligned_pos)%4;
613
+ --toparse;
614
+ break;
615
+ default:
616
+ ;
617
+ }
618
+ }
619
+
620
+
621
+ return pos <= (ring[0].len+ring[1].len) ? pos : 0;
622
+ }
623
+
624
+ size_t rtosc_message_length(const char *msg, size_t len)
625
+ {
626
+ ring_t ring[2] = {{(char*)msg,len},{NULL,0}};
627
+ return rtosc_message_ring_length(ring);
628
+ }
629
+
630
+ bool rtosc_valid_message_p(const char *msg, size_t len)
631
+ {
632
+ //Validate Path Characters (assumes printable characters are sufficient)
633
+ if(*msg != '/')
634
+ return false;
635
+ const char *tmp = msg;
636
+ for(unsigned i=0; i<len; ++i) {
637
+ if(*tmp == 0)
638
+ break;
639
+ if(!isprint(*tmp))
640
+ return false;
641
+ tmp++;
642
+ }
643
+
644
+ //tmp is now either pointing to a null or the end of the string
645
+ const size_t offset1 = tmp-msg;
646
+ size_t offset2 = tmp-msg;
647
+ for(; offset2<len; offset2++) {
648
+ if(*tmp == ',')
649
+ break;
650
+ tmp++;
651
+ }
652
+
653
+ //Too many NULL bytes
654
+ if(offset2-offset1 > 4)
655
+ return false;
656
+
657
+ if((offset2 % 4) != 0)
658
+ return false;
659
+
660
+ size_t observed_length = rtosc_message_length(msg, len);
661
+ return observed_length == len;
662
+ }
663
+ static uint64_t extract_uint64(const uint8_t *arg_pos)
664
+ {
665
+ uint64_t arg = 0;
666
+ arg |= (((uint64_t)*arg_pos++) << 56);
667
+ arg |= (((uint64_t)*arg_pos++) << 48);
668
+ arg |= (((uint64_t)*arg_pos++) << 40);
669
+ arg |= (((uint64_t)*arg_pos++) << 32);
670
+ arg |= (((uint64_t)*arg_pos++) << 24);
671
+ arg |= (((uint64_t)*arg_pos++) << 16);
672
+ arg |= (((uint64_t)*arg_pos++) << 8);
673
+ arg |= (((uint64_t)*arg_pos++));
674
+ return arg;
675
+ }
676
+
677
+ static uint32_t extract_uint32(const uint8_t *arg_pos)
678
+ {
679
+ uint32_t arg = 0;
680
+ arg |= (((uint32_t)*arg_pos++) << 24);
681
+ arg |= (((uint32_t)*arg_pos++) << 16);
682
+ arg |= (((uint32_t)*arg_pos++) << 8);
683
+ arg |= (((uint32_t)*arg_pos++));
684
+ return arg;
685
+ }
686
+
687
+ static void emplace_uint64(uint8_t *buffer, uint64_t d)
688
+ {
689
+ buffer[0] = ((d>>56) & 0xff);
690
+ buffer[1] = ((d>>48) & 0xff);
691
+ buffer[2] = ((d>>40) & 0xff);
692
+ buffer[3] = ((d>>32) & 0xff);
693
+ buffer[4] = ((d>>24) & 0xff);
694
+ buffer[5] = ((d>>16) & 0xff);
695
+ buffer[6] = ((d>>8) & 0xff);
696
+ buffer[7] = ((d>>0) & 0xff);
697
+ }
698
+
699
+ static void emplace_uint32(uint8_t *buffer, uint32_t d)
700
+ {
701
+ buffer[0] = ((d>>24) & 0xff);
702
+ buffer[1] = ((d>>16) & 0xff);
703
+ buffer[2] = ((d>>8) & 0xff);
704
+ buffer[3] = ((d>>0) & 0xff);
705
+ }
706
+
707
+ size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...)
708
+ {
709
+ char *_buffer = buffer;
710
+ memset(buffer, 0, len);
711
+ strcpy(buffer, "#bundle");
712
+ buffer += 8;
713
+ emplace_uint64((uint8_t*)buffer, tt);
714
+ buffer += 8;
715
+ va_list va;
716
+ va_start(va, elms);
717
+ for(int i=0; i<elms; ++i) {
718
+ const char *msg = va_arg(va, const char*);
719
+ //It is assumed that any passed message/bundle is valid
720
+ size_t size = rtosc_message_length(msg, -1);
721
+ emplace_uint32((uint8_t*)buffer, size);
722
+ buffer += 4;
723
+ memcpy(buffer, msg, size);
724
+ buffer+=size;
725
+ }
726
+ va_end(va);
727
+
728
+ return buffer-_buffer;
729
+ }
730
+
731
+
732
+ #define POS ((size_t)(((const char *)lengths) - buffer))
733
+ size_t rtosc_bundle_elements(const char *buffer, size_t len)
734
+ {
735
+ const uint32_t *lengths = (const uint32_t*) (buffer+16);
736
+ size_t elms = 0;
737
+ while(POS < len && extract_uint32((const uint8_t*)lengths)) {
738
+ lengths += extract_uint32((const uint8_t*)lengths)/4+1;
739
+
740
+ if(POS > len)
741
+ break;
742
+ ++elms;
743
+ }
744
+ return elms;
745
+ }
746
+ #undef POS
747
+
748
+ const char *rtosc_bundle_fetch(const char *buffer, unsigned elm)
749
+ {
750
+ const uint32_t *lengths = (const uint32_t*) (buffer+16);
751
+ size_t elm_pos = 0;
752
+ while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) {
753
+ ++elm_pos;
754
+ lengths += extract_uint32((const uint8_t*)lengths)/4+1;
755
+ }
756
+
757
+ return (const char*) (elm==elm_pos?lengths+1:NULL);
758
+ }
759
+
760
+ size_t rtosc_bundle_size(const char *buffer, unsigned elm)
761
+ {
762
+ const uint32_t *lengths = (const uint32_t*) (buffer+16);
763
+ size_t elm_pos = 0;
764
+ size_t last_len = 0;
765
+ while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) {
766
+ last_len = extract_uint32((const uint8_t*)lengths);
767
+ ++elm_pos, lengths+=extract_uint32((const uint8_t*)lengths)/4+1;
768
+ }
769
+
770
+ return last_len;
771
+ }
772
+
773
+ int rtosc_bundle_p(const char *msg)
774
+ {
775
+ return !strcmp(msg,"#bundle");
776
+ }
777
+
778
+ uint64_t rtosc_bundle_timetag(const char *msg)
779
+ {
780
+ return extract_uint64((const uint8_t*)msg+8);
781
+ }