|
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <ctype.h>
- #include <assert.h>
-
- #include <rtosc/rtosc.h>
-
- const char *rtosc_argument_string(const char *msg)
- {
- assert(msg && *msg);
- while(*++msg); //skip pattern
- while(!*++msg);//skip null
- return msg+1; //skip comma
- }
-
- unsigned rtosc_narguments(const char *msg)
- {
- const char *args = rtosc_argument_string(msg);
- int nargs = 0;
- while(*args++)
- nargs += (*args == ']' || *args == '[') ? 0 : 1;
- return nargs;
- }
-
- static int has_reserved(char type)
- {
- switch(type)
- {
- case 'i'://official types
- case 's':
- case 'b':
- case 'f':
-
- case 'h'://unofficial
- case 't':
- case 'd':
- case 'S':
- case 'r':
- case 'm':
- case 'c':
- return 1;
- case 'T':
- case 'F':
- case 'N':
- case 'I':
- case '[':
- case ']':
- return 0;
- }
-
- //Should not happen
- return 0;
- }
-
- static unsigned nreserved(const char *args)
- {
- unsigned res = 0;
- for(;*args;++args)
- res += has_reserved(*args);
-
- return res;
- }
-
- char rtosc_type(const char *msg, unsigned nargument)
- {
- assert(nargument < rtosc_narguments(msg));
- const char *arg = rtosc_argument_string(msg);
- while(1) {
- if(*arg == '[' || *arg == ']')
- ++arg;
- else if(!nargument || !*arg)
- return *arg;
- else
- ++arg, --nargument;
- }
- }
-
- static unsigned arg_start(const char *msg_)
- {
- const uint8_t *msg = (const uint8_t*)msg_;
- //Iterate to the right position
- const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg_);
- const uint8_t *aligned_ptr = args-1;
- const uint8_t *arg_pos = args;
-
- while(*++arg_pos);
- //Alignment
- arg_pos += 4-(arg_pos-aligned_ptr)%4;
- return arg_pos-msg;
- }
-
- static unsigned arg_size(const uint8_t *arg_mem, char type)
- {
- if(!has_reserved(type))
- return 0;
- const uint8_t *arg_pos=arg_mem;
- uint32_t blob_length = 0;
- switch(type)
- {
- case 'h':
- case 't':
- case 'd':
- return 8;
- case 'm':
- case 'r':
- case 'f':
- case 'c':
- case 'i':
- return 4;
- case 'S':
- case 's':
- while(*++arg_pos);
- arg_pos += 4-(arg_pos-arg_mem)%4;
- return arg_pos-arg_mem;
- case 'b':
- blob_length |= (*arg_pos++ << 24);
- blob_length |= (*arg_pos++ << 16);
- blob_length |= (*arg_pos++ << 8);
- blob_length |= (*arg_pos++);
- if(blob_length%4)
- blob_length += 4-blob_length%4;
- arg_pos += blob_length;
- return arg_pos-arg_mem;
- default:
- assert("Invalid Type");
- }
- return -1;
- }
-
- static unsigned arg_off(const char *msg, unsigned idx)
- {
- if(!has_reserved(rtosc_type(msg,idx)))
- return 0;
-
- //Iterate to the right position
- const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg);
- const uint8_t *aligned_ptr = args-1;
- const uint8_t *arg_pos = args;
-
- while(*++arg_pos);
- //Alignment
- arg_pos += 4-(arg_pos-((uint8_t*)aligned_ptr))%4;
-
- //ignore any leading '[' or ']'
- while(*args == '[' || *args == ']')
- ++args;
-
- while(idx--) {
- char type = *args++;
- if(type == '[' || type == ']')
- idx++;//not a valid arg idx
- else
- arg_pos += arg_size(arg_pos, type);
- }
- return arg_pos-(uint8_t*)msg;
- }
-
- size_t rtosc_message(char *buffer,
- size_t len,
- const char *address,
- const char *arguments,
- ...)
- {
- va_list va;
- va_start(va, arguments);
- size_t result = rtosc_vmessage(buffer, len, address, arguments, va);
- va_end(va);
- return result;
- }
-
- //Calculate the size of the message without writing to a buffer
- static size_t vsosc_null(const char *address,
- const char *arguments,
- const rtosc_arg_t *args)
- {
- unsigned pos = 0;
- pos += strlen(address);
- pos += 4-pos%4;//get 32 bit alignment
- pos += 1+strlen(arguments);
- pos += 4-pos%4;
-
- unsigned toparse = nreserved(arguments);
- unsigned arg_pos = 0;
-
- //Take care of varargs
- while(toparse)
- {
- char arg = *arguments++;
- assert(arg);
- int i;
- const char *s;
- switch(arg) {
- case 'h':
- case 't':
- case 'd':
- ++arg_pos;
- pos += 8;
- --toparse;
- break;
- case 'm':
- case 'r':
- case 'c':
- case 'f':
- case 'i':
- ++arg_pos;
- pos += 4;
- --toparse;
- break;
- case 's':
- case 'S':
- s = args[arg_pos++].s;
- assert(s && "Input strings CANNOT be NULL");
- pos += strlen(s);
- pos += 4-pos%4;
- --toparse;
- break;
- case 'b':
- i = args[arg_pos++].b.len;
- pos += 4 + i;
- if(pos%4)
- pos += 4-pos%4;
- --toparse;
- break;
- default:
- ;
- }
- }
-
- return pos;
- }
- size_t rtosc_vmessage(char *buffer,
- size_t len,
- const char *address,
- const char *arguments,
- va_list ap)
- {
- const unsigned nargs = nreserved(arguments);
- if(!nargs)
- return rtosc_amessage(buffer,len,address,arguments,NULL);
-
- rtosc_arg_t args[nargs];
-
- unsigned arg_pos = 0;
- const char *arg_str = arguments;
- uint8_t *midi_tmp;
- while(arg_pos < nargs)
- {
- switch(*arg_str++) {
- case 'h':
- case 't':
- args[arg_pos++].h = va_arg(ap, int64_t);
- break;
- case 'd':
- args[arg_pos++].d = va_arg(ap, double);
- break;
- case 'c':
- case 'i':
- case 'r':
- args[arg_pos++].i = va_arg(ap, int);
- break;
- case 'm':
- midi_tmp = va_arg(ap, uint8_t *);
- args[arg_pos].m[0] = midi_tmp[0];
- args[arg_pos].m[1] = midi_tmp[1];
- args[arg_pos].m[2] = midi_tmp[2];
- args[arg_pos++].m[3] = midi_tmp[3];
- break;
- case 'S':
- case 's':
- args[arg_pos++].s = va_arg(ap, const char *);
- break;
- case 'b':
- args[arg_pos].b.len = va_arg(ap, int);
- args[arg_pos].b.data = va_arg(ap, unsigned char *);
- arg_pos++;
- break;
- case 'f':
- args[arg_pos++].f = va_arg(ap, double);
- break;
- default:
- ;
- }
- }
-
- return rtosc_amessage(buffer,len,address,arguments,args);
- }
-
- size_t rtosc_amessage(char *buffer,
- size_t len,
- const char *address,
- const char *arguments,
- const rtosc_arg_t *args)
- {
- const size_t total_len = vsosc_null(address, arguments, args);
-
- if(!buffer)
- return total_len;
-
- //Abort if the message cannot fit
- if(total_len>len) {
- memset(buffer, 0, len);
- return 0;
- }
-
- memset(buffer, 0, total_len);
-
- unsigned pos = 0;
- while(*address)
- buffer[pos++] = *address++;
-
- //get 32 bit alignment
- pos += 4-pos%4;
-
- buffer[pos++] = ',';
-
- const char *arg_str = arguments;
- while(*arg_str)
- buffer[pos++] = *arg_str++;
-
- pos += 4-pos%4;
-
- unsigned toparse = nreserved(arguments);
- unsigned arg_pos = 0;
- while(toparse)
- {
- char arg = *arguments++;
- assert(arg);
- int32_t i;
- int64_t d;
- const uint8_t *m;
- const char *s;
- const unsigned char *u;
- rtosc_blob_t b;
- switch(arg) {
- case 'h':
- case 't':
- case 'd':
- d = args[arg_pos++].t;
- buffer[pos++] = ((d>>56) & 0xff);
- buffer[pos++] = ((d>>48) & 0xff);
- buffer[pos++] = ((d>>40) & 0xff);
- buffer[pos++] = ((d>>32) & 0xff);
- buffer[pos++] = ((d>>24) & 0xff);
- buffer[pos++] = ((d>>16) & 0xff);
- buffer[pos++] = ((d>>8) & 0xff);
- buffer[pos++] = (d & 0xff);
- --toparse;
- break;
- case 'r':
- case 'f':
- case 'c':
- case 'i':
- i = args[arg_pos++].i;
- buffer[pos++] = ((i>>24) & 0xff);
- buffer[pos++] = ((i>>16) & 0xff);
- buffer[pos++] = ((i>>8) & 0xff);
- buffer[pos++] = (i & 0xff);
- --toparse;
- break;
- case 'm':
- //TODO verify ordering of spec
- m = args[arg_pos++].m;
- buffer[pos++] = m[0];
- buffer[pos++] = m[1];
- buffer[pos++] = m[2];
- buffer[pos++] = m[3];
- --toparse;
- break;
- case 'S':
- case 's':
- s = args[arg_pos++].s;
- while(*s)
- buffer[pos++] = *s++;
- pos += 4-pos%4;
- --toparse;
- break;
- case 'b':
- b = args[arg_pos++].b;
- i = b.len;
- buffer[pos++] = ((i>>24) & 0xff);
- buffer[pos++] = ((i>>16) & 0xff);
- buffer[pos++] = ((i>>8) & 0xff);
- buffer[pos++] = (i & 0xff);
- u = b.data;
- if(u) {
- while(i--)
- buffer[pos++] = *u++;
- }
- else
- pos += i;
- if(pos%4)
- pos += 4-pos%4;
- --toparse;
- break;
- default:
- ;
- }
- }
-
- return pos;
- }
-
- static rtosc_arg_t extract_arg(const uint8_t *arg_pos, char type)
- {
- rtosc_arg_t result = {0};
- //trivial case
- if(!has_reserved(type)) {
- switch(type)
- {
- case 'T':
- result.T = true;
- break;
- case 'F':
- result.T = false;
- break;
- default:
- ;
- }
- } else {
- switch(type)
- {
- case 'h':
- case 't':
- case 'd':
- result.t |= (((uint64_t)*arg_pos++) << 56);
- result.t |= (((uint64_t)*arg_pos++) << 48);
- result.t |= (((uint64_t)*arg_pos++) << 40);
- result.t |= (((uint64_t)*arg_pos++) << 32);
- result.t |= (((uint64_t)*arg_pos++) << 24);
- result.t |= (((uint64_t)*arg_pos++) << 16);
- result.t |= (((uint64_t)*arg_pos++) << 8);
- result.t |= (((uint64_t)*arg_pos++));
- break;
- case 'r':
- case 'f':
- case 'c':
- case 'i':
- result.i |= (*arg_pos++ << 24);
- result.i |= (*arg_pos++ << 16);
- result.i |= (*arg_pos++ << 8);
- result.i |= (*arg_pos++);
- break;
- case 'm':
- result.m[0] = *arg_pos++;
- result.m[1] = *arg_pos++;
- result.m[2] = *arg_pos++;
- result.m[3] = *arg_pos++;
- break;
- case 'b':
- result.b.len |= (*arg_pos++ << 24);
- result.b.len |= (*arg_pos++ << 16);
- result.b.len |= (*arg_pos++ << 8);
- result.b.len |= (*arg_pos++);
- result.b.data = (unsigned char *)arg_pos;
- break;
- case 'S':
- case 's':
- result.s = (char *)arg_pos;
- break;
- }
- }
-
- return result;
- }
-
- static const char *advance_past_dummy_args(const char *args)
- {
- while(*args == '[' || *args == ']')
- args++;
- return args;
- }
-
- rtosc_arg_itr_t rtosc_itr_begin(const char *msg)
- {
- rtosc_arg_itr_t itr;
- itr.type_pos = advance_past_dummy_args(rtosc_argument_string(msg));
- itr.value_pos = (uint8_t*)(msg+arg_start(msg));
-
- return itr;
- }
-
- rtosc_arg_val_t rtosc_itr_next(rtosc_arg_itr_t *itr)
- {
- //current position provides the value
- rtosc_arg_val_t result = {0,{0}};
- result.type = *itr->type_pos;
- if(result.type)
- result.val = extract_arg(itr->value_pos, result.type);
-
- //advance
- itr->type_pos = advance_past_dummy_args(itr->type_pos+1);
- char type = result.type;
- int size = arg_size(itr->value_pos, type);
- itr->value_pos += size;
-
-
- return result;
- }
-
- int rtosc_itr_end(rtosc_arg_itr_t itr)
- {
- return !itr.type_pos || !*itr.type_pos;
- }
-
- rtosc_arg_t rtosc_argument(const char *msg, unsigned idx)
- {
- char type = rtosc_type(msg, idx);
- uint8_t *arg_mem = (uint8_t*)msg + arg_off(msg, idx);
- return extract_arg(arg_mem, type);
- }
-
- static unsigned char deref(unsigned pos, ring_t *ring)
- {
- return pos<ring[0].len ? ring[0].data[pos] :
- ((pos-ring[0].len)<ring[1].len ? ring[1].data[pos-ring[0].len] : 0x00);
- }
-
- static size_t bundle_ring_length(ring_t *ring)
- {
- unsigned pos = 8+8;//goto first length field
- uint32_t advance = 0;
- do {
- advance = deref(pos+0, ring) << (8*3) |
- deref(pos+1, ring) << (8*2) |
- deref(pos+2, ring) << (8*1) |
- deref(pos+3, ring) << (8*0);
- if(advance)
- pos += 4+advance;
- } while(advance);
-
- return pos <= (ring[0].len+ring[1].len) ? pos : 0;
- }
-
- //Zero means no full message present
- size_t rtosc_message_ring_length(ring_t *ring)
- {
- //Check if the message is a bundle
- if(deref(0,ring) == '#' &&
- deref(1,ring) == 'b' &&
- deref(2,ring) == 'u' &&
- deref(3,ring) == 'n' &&
- deref(4,ring) == 'd' &&
- deref(5,ring) == 'l' &&
- deref(6,ring) == 'e' &&
- deref(7,ring) == '\0')
- return bundle_ring_length(ring);
-
- //Proceed for normal messages
- //Consume path
- unsigned pos = 0;
- while(deref(pos++,ring));
- pos--;
-
- //Travel through the null word end [1..4] bytes
- for(int i=0; i<4; ++i)
- if(deref(++pos, ring))
- break;
-
- if(deref(pos, ring) != ',')
- return 0;
-
- unsigned aligned_pos = pos;
- int arguments = pos+1;
- while(deref(++pos,ring));
- pos += 4-(pos-aligned_pos)%4;
-
- unsigned toparse = 0;
- {
- int arg = arguments-1;
- while(deref(++arg,ring))
- toparse += has_reserved(deref(arg,ring));
- }
-
- //Take care of varargs
- while(toparse)
- {
- char arg = deref(arguments++,ring);
- assert(arg);
- uint32_t i;
- switch(arg) {
- case 'h':
- case 't':
- case 'd':
- pos += 8;
- --toparse;
- break;
- case 'm':
- case 'r':
- case 'c':
- case 'f':
- case 'i':
- pos += 4;
- --toparse;
- break;
- case 'S':
- case 's':
- while(deref(++pos,ring));
- pos += 4-(pos-aligned_pos)%4;
- --toparse;
- break;
- case 'b':
- i = 0;
- i |= (deref(pos++,ring) << 24);
- i |= (deref(pos++,ring) << 16);
- i |= (deref(pos++,ring) << 8);
- i |= (deref(pos++,ring));
- pos += i;
- if((pos-aligned_pos)%4)
- pos += 4-(pos-aligned_pos)%4;
- --toparse;
- break;
- default:
- ;
- }
- }
-
-
- return pos <= (ring[0].len+ring[1].len) ? pos : 0;
- }
-
- size_t rtosc_message_length(const char *msg, size_t len)
- {
- ring_t ring[2] = {{(char*)msg,len},{NULL,0}};
- return rtosc_message_ring_length(ring);
- }
-
- bool rtosc_valid_message_p(const char *msg, size_t len)
- {
- //Validate Path Characters (assumes printable characters are sufficient)
- if(*msg != '/')
- return false;
- const char *tmp = msg;
- for(unsigned i=0; i<len; ++i) {
- if(*tmp == 0)
- break;
- if(!isprint(*tmp))
- return false;
- tmp++;
- }
-
- //tmp is now either pointing to a null or the end of the string
- const size_t offset1 = tmp-msg;
- size_t offset2 = tmp-msg;
- for(; offset2<len; offset2++) {
- if(*tmp == ',')
- break;
- tmp++;
- }
-
- //Too many NULL bytes
- if(offset2-offset1 > 4)
- return false;
-
- if((offset2 % 4) != 0)
- return false;
-
- size_t observed_length = rtosc_message_length(msg, len);
- return observed_length == len;
- }
- static uint64_t extract_uint64(const uint8_t *arg_pos)
- {
- uint64_t arg = 0;
- arg |= (((uint64_t)*arg_pos++) << 56);
- arg |= (((uint64_t)*arg_pos++) << 48);
- arg |= (((uint64_t)*arg_pos++) << 40);
- arg |= (((uint64_t)*arg_pos++) << 32);
- arg |= (((uint64_t)*arg_pos++) << 24);
- arg |= (((uint64_t)*arg_pos++) << 16);
- arg |= (((uint64_t)*arg_pos++) << 8);
- arg |= (((uint64_t)*arg_pos++));
- return arg;
- }
-
- static uint32_t extract_uint32(const uint8_t *arg_pos)
- {
- uint32_t arg = 0;
- arg |= (((uint32_t)*arg_pos++) << 24);
- arg |= (((uint32_t)*arg_pos++) << 16);
- arg |= (((uint32_t)*arg_pos++) << 8);
- arg |= (((uint32_t)*arg_pos++));
- return arg;
- }
-
- static void emplace_uint64(uint8_t *buffer, uint64_t d)
- {
- buffer[0] = ((d>>56) & 0xff);
- buffer[1] = ((d>>48) & 0xff);
- buffer[2] = ((d>>40) & 0xff);
- buffer[3] = ((d>>32) & 0xff);
- buffer[4] = ((d>>24) & 0xff);
- buffer[5] = ((d>>16) & 0xff);
- buffer[6] = ((d>>8) & 0xff);
- buffer[7] = ((d>>0) & 0xff);
- }
-
- static void emplace_uint32(uint8_t *buffer, uint32_t d)
- {
- buffer[0] = ((d>>24) & 0xff);
- buffer[1] = ((d>>16) & 0xff);
- buffer[2] = ((d>>8) & 0xff);
- buffer[3] = ((d>>0) & 0xff);
- }
-
- size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...)
- {
- char *_buffer = buffer;
- memset(buffer, 0, len);
- strcpy(buffer, "#bundle");
- buffer += 8;
- emplace_uint64((uint8_t*)buffer, tt);
- buffer += 8;
- va_list va;
- va_start(va, elms);
- for(int i=0; i<elms; ++i) {
- const char *msg = va_arg(va, const char*);
- //It is assumed that any passed message/bundle is valid
- size_t size = rtosc_message_length(msg, -1);
- emplace_uint32((uint8_t*)buffer, size);
- buffer += 4;
- memcpy(buffer, msg, size);
- buffer+=size;
- }
- va_end(va);
-
- return buffer-_buffer;
- }
-
-
- #define POS ((size_t)(((const char *)lengths) - buffer))
- size_t rtosc_bundle_elements(const char *buffer, size_t len)
- {
- const uint32_t *lengths = (const uint32_t*) (buffer+16);
- size_t elms = 0;
- while(POS < len && extract_uint32((const uint8_t*)lengths)) {
- lengths += extract_uint32((const uint8_t*)lengths)/4+1;
-
- if(POS > len)
- break;
- ++elms;
- }
- return elms;
- }
- #undef POS
-
- const char *rtosc_bundle_fetch(const char *buffer, unsigned elm)
- {
- const uint32_t *lengths = (const uint32_t*) (buffer+16);
- size_t elm_pos = 0;
- while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) {
- ++elm_pos;
- lengths += extract_uint32((const uint8_t*)lengths)/4+1;
- }
-
- return (const char*) (elm==elm_pos?lengths+1:NULL);
- }
-
- size_t rtosc_bundle_size(const char *buffer, unsigned elm)
- {
- const uint32_t *lengths = (const uint32_t*) (buffer+16);
- size_t elm_pos = 0;
- size_t last_len = 0;
- while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) {
- last_len = extract_uint32((const uint8_t*)lengths);
- ++elm_pos, lengths+=extract_uint32((const uint8_t*)lengths)/4+1;
- }
-
- return last_len;
- }
-
- int rtosc_bundle_p(const char *msg)
- {
- return !strcmp(msg,"#bundle");
- }
-
- uint64_t rtosc_bundle_timetag(const char *msg)
- {
- return extract_uint64((const uint8_t*)msg+8);
- }
|