#include #include #include #include #include #include #include #include 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 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 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); }