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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +48 -0
- data/README.md +95 -0
- data/Rakefile +5 -0
- data/ext/fast_osc/extconf.rb +28 -0
- data/ext/fast_osc/fast_osc_wrapper.c +155 -0
- data/ext/fast_osc/rtosc.c +781 -0
- data/ext/fast_osc/rtosc.h +269 -0
- data/fast_osc.gemspec +26 -0
- data/lib/fast_osc/version.rb +3 -0
- data/lib/fast_osc.rb +6 -0
- metadata +100 -0
@@ -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
|
+
}
|