#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; } static const rtosc_cmp_options default_cmp_options = { 0.0 }; int rtosc_arg_vals_eq(rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, size_t lsize, size_t rsize, const rtosc_cmp_options* opt) { #define mfabs(val) (((val) >= 0) ? (val) : -(val)) if(!opt) opt = &default_cmp_options; if(lsize != rsize) return 0; int rval = 1; for(size_t i = 0; i < lsize && rval; ++i, ++lhs, ++rhs) { if(lhs->type == rhs->type) switch(lhs->type) { case 'i': case 'c': case 'r': rval = lhs->val.i == rhs->val.i; break; case 'I': case 'T': case 'F': case 'N': rval = 1; break; case 'f': rval = (opt->float_tolerance == 0.0) ? lhs->val.f == rhs->val.f : mfabs(lhs->val.f - rhs->val.f) <= (float)opt->float_tolerance; break; case 'd': rval = (opt->float_tolerance == 0.0) ? lhs->val.d == rhs->val.d : mfabs(lhs->val.d - rhs->val.d) <= opt->float_tolerance; break; case 'h': rval = lhs->val.h == rhs->val.h; break; case 't': rval = lhs->val.t == rhs->val.t; break; case 'm': rval = 0 == memcmp(lhs->val.m, rhs->val.m, 4); break; case 's': case 'S': rval = (lhs->val.s == NULL || rhs->val.s == NULL) ? lhs->val.s == rhs->val.s : (0 == strcmp(lhs->val.s, rhs->val.s)); break; case 'b': { int32_t lbs = lhs->val.b.len, rbs = rhs->val.b.len; rval = lbs == rbs; if(rval) rval = 0 == memcmp(lhs->val.b.data, rhs->val.b.data, lbs); break; } } else { rval = 0; } } return rval; #undef mfabs } int rtosc_arg_vals_cmp(rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, size_t lsize, size_t rsize, const rtosc_cmp_options* opt) { #define cmp_3way(val1,val2) (((val1) == (val2)) \ ? 0 \ : (((val1) > (val2)) ? 1 : -1)) #define mfabs(val) (((val) >= 0) ? (val) : -(val)) if(!opt) opt = &default_cmp_options; size_t rval = 0; size_t min = lsize > rsize ? rsize : lsize; for(size_t i = 0; i < min && !rval; ++i, ++lhs, ++rhs) { if(lhs->type == rhs->type) switch(lhs->type) { case 'i': case 'c': case 'r': rval = cmp_3way(lhs->val.i, rhs->val.i); break; case 'I': case 'T': case 'F': case 'N': rval = 0; break; case 'f': rval = (opt->float_tolerance == 0.0) ? cmp_3way(lhs->val.f, rhs->val.f) : (mfabs(lhs->val.f - rhs->val.f) <= (float)opt->float_tolerance) ? 0 : ((lhs->val.f > rhs->val.f) ? 1 : -1); break; case 'd': rval = (opt->float_tolerance == 0.0) ? cmp_3way(lhs->val.d, rhs->val.d) : (mfabs(lhs->val.d - rhs->val.d) <= opt->float_tolerance) ? 0 : ((lhs->val.d > rhs->val.d) ? 1 : -1); break; case 'h': rval = cmp_3way(lhs->val.h, rhs->val.h); break; case 't': // immediately is considered lower than everything else // this means if you send two events to a client, // one being "immediately" and one being different, the // immediately-event has the higher priority, event if the // other one is in the past rval = (lhs->val.t == 1) ? (rhs->val.t == 1) ? 0 : -1 // lhs has higher priority => lhs < rhs : (rhs->val.t == 1) ? 1 : cmp_3way(lhs->val.t, rhs->val.t); break; case 'm': rval = memcmp(lhs->val.m, rhs->val.m, 4); break; case 's': case 'S': rval = (lhs->val.s == NULL || rhs->val.s == NULL) ? cmp_3way(lhs->val.s, rhs->val.s) : strcmp(lhs->val.s, rhs->val.s); break; case 'b': { int32_t lbs = lhs->val.b.len, rbs = rhs->val.b.len; int32_t minlen = (lbs < rbs) ? lbs : rbs; rval = memcmp(lhs->val.b.data, rhs->val.b.data, minlen); if(lbs != rbs && !rval) { // both equal until here // the string that ends here is lexicographically smaller rval = (lbs > rbs) ? lhs->val.b.data[minlen] : -rhs->val.b.data[minlen]; } else return rval; break; } } else { rval = (lhs->type > rhs->type) ? 1 : -1; } } if(rval == 0 && lsize != rsize) { return (lsize > rsize) ? 1 : -1; } return rval; #undef mfabs #undef cmp_3way } void rtosc_v2args(rtosc_arg_t* args, size_t nargs, const char* arg_str, rtosc_va_list_t* ap) { unsigned arg_pos = 0; uint8_t *midi_tmp; while(arg_pos < nargs) { switch(*arg_str++) { case 'h': case 't': args[arg_pos++].h = va_arg(ap->a, int64_t); break; case 'd': args[arg_pos++].d = va_arg(ap->a, double); break; case 'c': case 'i': case 'r': args[arg_pos++].i = va_arg(ap->a, int); break; case 'm': midi_tmp = va_arg(ap->a, 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->a, const char *); break; case 'b': args[arg_pos].b.len = va_arg(ap->a, int); args[arg_pos].b.data = va_arg(ap->a, unsigned char *); arg_pos++; break; case 'f': args[arg_pos++].f = va_arg(ap->a, double); break; case 'T': case 'F': case 'N': case 'I': args[arg_pos++].T = arg_str[-1]; break; default: ; } } } void rtosc_2args(rtosc_arg_t* args, size_t nargs, const char* arg_str, ...) { rtosc_va_list_t va; va_start(va.a, arg_str); rtosc_v2args(args, nargs, arg_str, &va); va_end(va.a); } void rtosc_v2argvals(rtosc_arg_val_t* args, size_t nargs, const char* arg_str, va_list ap) { rtosc_va_list_t ap2; va_copy(ap2.a, ap); for(size_t i=0; itype = *arg_str; rtosc_v2args(&args->val, 1, arg_str, &ap2); } } void rtosc_2argvals(rtosc_arg_val_t* args, size_t nargs, const char* arg_str, ...) { va_list va; va_start(va, arg_str); rtosc_v2argvals(args, nargs, arg_str, va); va_end(va); } 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]; rtosc_va_list_t ap2; va_copy(ap2.a, ap); rtosc_v2args(args, nargs, arguments, &ap2); 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); }