#include "ports.h" #include #include #include #include #include using namespace rtosc; static inline void scat(char *dest, const char *src) { while(*dest) dest++; if(*dest) dest++; while(*src && *src!=':') *dest++ = *src++; *dest = 0; } RtData::RtData(void) :loc(NULL), loc_size(0), obj(NULL), matches(0) {} void RtData::reply(const char *path, const char *args, ...) { va_list va; va_start(va,args); char buffer[1024]; rtosc_vmessage(buffer,1024,path,args,va); reply(buffer); va_end(va); }; void RtData::reply(const char *msg) {(void)msg;}; void RtData::broadcast(const char *path, const char *args, ...) { va_list va; va_start(va,args); char buffer[1024]; rtosc_vmessage(buffer,1024,path,args,va); broadcast(buffer); va_end(va); } void RtData::broadcast(const char *msg) {reply(msg);}; void metaiterator_advance(const char *&title, const char *&value) { if(!title || !*title) { value = NULL; return; } //Try to find "\0=" after title string value = title; while(*value) ++value; if(*++value != '=') value = NULL; else value++; } Port::MetaIterator::MetaIterator(const char *str) :title(str), value(NULL) { metaiterator_advance(title, value); } Port::MetaIterator& Port::MetaIterator::operator++(void) { if(!title || !*title) { title = NULL; return *this; } //search for next parameter start //aka "\0:" unless "\0\0" is seen char prev = 0; while(prev || (*title && *title != ':')) prev = *title++; if(!*title) title = NULL; else ++title; metaiterator_advance(title, value); return *this; } Port::MetaContainer::MetaContainer(const char *str_) :str_ptr(str_) {} Port::MetaIterator Port::MetaContainer::begin(void) const { if(str_ptr && *str_ptr == ':') return Port::MetaIterator(str_ptr+1); else return Port::MetaIterator(str_ptr); } Port::MetaIterator Port::MetaContainer::end(void) const { return MetaIterator(NULL); } Port::MetaIterator Port::MetaContainer::find(const char *str) const { for(const auto x : *this) if(!strcmp(x.title, str)) return x; return NULL; } size_t Port::MetaContainer::length(void) const { if(!str_ptr || !*str_ptr) return 0; char prev = 0; const char *itr = str_ptr; while(prev || *itr) prev = *itr++; return 2+(itr-str_ptr); } const char *Port::MetaContainer::operator[](const char *str) const { for(const auto x : *this) if(!strcmp(x.title, str)) return x.value; return NULL; } //Match the arg string or fail inline bool arg_matcher(const char *pattern, const char *args) { //match anything if now arg restriction is present (ie the ':') if(*pattern++ != ':') return true; const char *arg_str = args; bool arg_match = *pattern || *pattern == *arg_str; while(*pattern && *pattern != ':') arg_match &= (*pattern++==*arg_str++); if(*pattern==':') { if(arg_match && !*arg_str) return true; else return arg_matcher(pattern, args); //retry } return arg_match; } inline bool scmp(const char *a, const char *b) { while(*a && *a == *b) a++, b++; return a[0] == b[0]; } typedef std::vector words_t; typedef std::vector svec_t; typedef std::vector cvec_t; typedef std::vector ivec_t; typedef std::vector tuple_t; typedef std::vector tvec_t; namespace rtosc{ class Port_Matcher { public: bool *enump; svec_t fixed; cvec_t arg_spec; ivec_t pos; ivec_t assoc; ivec_t remap; bool rtosc_match_args(const char *pattern, const char *msg) { //match anything if now arg restriction is present //(ie the ':') if(*pattern++ != ':') return true; const char *arg_str = rtosc_argument_string(msg); bool arg_match = *pattern || *pattern == *arg_str; while(*pattern && *pattern != ':') arg_match &= (*pattern++==*arg_str++); if(*pattern==':') { if(arg_match && !*arg_str) return true; else return rtosc_match_args(pattern, msg); //retry } return arg_match; } bool hard_match(int i, const char *msg) { if(strncmp(msg, fixed[i].c_str(), fixed[i].length())) return false; if(arg_spec[i]) return rtosc_match_args(arg_spec[i], msg); else return true; } }; } tvec_t do_hash(const words_t &strs, const ivec_t &pos) { tvec_t tvec; for(auto &s:strs) { tuple_t tuple; tuple.push_back(s.length()); for(const auto &p:pos) if(p < (int)s.size()) tuple.push_back(s[p]); tvec.push_back(std::move(tuple)); } return tvec; } template int count_dups(std::vector &t) { int dups = 0; int N = t.size(); bool mark[t.size()]; memset(mark, 0, N); for(int i=0; i bool has(T &t, Z&z) { for(auto tt:t) if(tt==z) return true; return false; } int rtosc_max(int a, int b) { return a= current_dups) break; current_dups = pos_best_val; pos.push_back(pos_best); } auto hashed = do_hash(strs, pos); int d = count_dups(hashed); //printf("Total Dups: %d\n", d); if(d != 0) pos.clear(); return pos; } ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc) { ivec_t ivec; ivec.reserve(strs.size()); for(auto &s:strs) { int t = s.length(); for(auto p:pos) if(p < (int)s.size()) t += assoc[s[p]]; ivec.push_back(t); } return ivec; } ivec_t find_assoc(const words_t &strs, const ivec_t &pos) { ivec_t assoc; int current_dups = strs.size(); int N = 127; std::vector useful_chars; for(auto w:strs) for(auto c:w) if(!has(useful_chars, c)) useful_chars.push_back(c); for(int i=0; i= current_dups) break; current_dups = assoc_best_val; } auto hashed = do_hash(strs, pos, assoc); //int d = count_dups(hashed); //printf("Total Dups Assoc: %d\n", d); return assoc; } ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) { ivec_t remap; auto hashed = do_hash(strs, pos, assoc); //for(int i=0; i str, Port_Matcher &pm) { pm.pos = find_pos(str); if(pm.pos.empty()) { fprintf(stderr, "rtosc: Failed to generate minimal hash\n"); return; } pm.assoc = find_assoc(str, pm.pos); pm.remap = find_remap(str, pm.pos, pm.assoc); } void generate_minimal_hash(Ports &p, Port_Matcher &pm) { svec_t keys; cvec_t args; bool enump = false; for(unsigned i=0; i 0) { arg = p.ports[i].name+idx; tmp = tmp.substr(0,idx); } keys.push_back(tmp); args.push_back(arg); } pm.fixed = keys; pm.arg_spec = args; generate_minimal_hash(keys, pm); } Ports::Ports(std::initializer_list l) :ports(l), impl(new Port_Matcher) { generate_minimal_hash(*this, *impl); impl->enump = new bool[ports.size()]; for(int i=0; i<(int)ports.size(); ++i) impl->enump[i] = strchr(ports[i].name, '#'); elms = ports.size(); } Ports::~Ports() { delete []impl->enump; delete impl; } #if !defined(__GNUC__) #define __builtin_expect(a,b) a #endif void Ports::dispatch(const char *m, rtosc::RtData &d) const { void *obj = d.obj; //simple case if(!d.loc || !d.loc_size) { for(const Port &port: ports) { if(rtosc_match(port.name,m)) d.port = &port, port.cb(m,d), d.obj = obj; } } else { //TODO this function is certainly buggy at the moment, some tests //are needed to make it clean //XXX buffer_size is not properly handled yet if(__builtin_expect(d.loc[0] == 0, 0)) { memset(d.loc, 0, d.loc_size); d.loc[0] = '/'; } char *old_end = d.loc; while(*old_end) ++old_end; if(impl->pos.empty()) { //No perfect minimal hash function for(unsigned i=0; ipos) if(p < (int)len) t += impl->assoc[m[p]]; if(t >= (int)impl->remap.size()) return; int port_num = impl->remap[t]; //Verify the chosen port is correct if(__builtin_expect(impl->hard_match(port_num, m), 1)) { const Port &port = ports[impl->remap[t]]; if(!port.ports) d.matches++; //Append the path if(impl->enump[port_num]) { const char *msg = m; char *pos = old_end; while(*msg && *msg != '/') *pos++ = *msg++; if(strchr(port.name, '/')) *pos++ = '/'; *pos = '\0'; } else memcpy(old_end, impl->fixed[port_num].c_str(), impl->fixed[port_num].length()+1); d.port = &port; //Apply callback port.cb(m,d), d.obj = obj; //Remove the rest of the path old_end[0] = '\0'; } } } } const Port *Ports::operator[](const char *name) const { for(const Port &port:ports) { const char *_needle = name, *_haystack = port.name; while(*_needle && *_needle==*_haystack)_needle++,_haystack++; if(*_needle == 0 && (*_haystack == ':' || *_haystack == '\0')) { return &port; } } return NULL; } static msg_t snip(msg_t m) { while(*m && *m != '/') ++m; return m+1; } const Port *Ports::apropos(const char *path) const { if(path && path[0] == '/') ++path; for(const Port &port: ports) if(strchr(port.name,'/') && rtosc_match_path(port.name,path)) return (strchr(path,'/')[1]==0) ? &port : port.ports->apropos(snip(path)); //This is the lowest level, now find the best port for(const Port &port: ports) if(*path && strstr(port.name, path)==port.name) return &port; return NULL; } void rtosc::walk_ports(const Ports *base, char *name_buffer, size_t buffer_size, void *data, port_walker_t walker) { assert(name_buffer); //XXX buffer_size is not properly handled yet if(name_buffer[0] == 0) name_buffer[0] = '/'; char *old_end = name_buffer; while(*old_end) ++old_end; for(const Port &p: *base) { if(strchr(p.name, '/')) {//it is another tree if(strchr(p.name,'#')) { const char *name = p.name; char *pos = old_end; while(*name != '#') *pos++ = *name++; const unsigned max = atoi(name+1); for(unsigned i=0; imeta(); if(meta.find("parameter") != p->meta().end()) { char type = 0; const char *foo = strchr(p->name, ':'); if(strchr(foo, 'f')) type = 'f'; else if(strchr(foo, 'i')) type = 'i'; else if(strchr(foo, 'c')) type = 'c'; else if(strchr(foo, 'T')) type = 't'; if(!type) { fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", p->name); return; } if(type == 't') { o << " \n"; o << " Enable " << p->meta()["documentation"] << "\n"; o << " \n"; o << " \n"; o << " \n"; o << " Disable " << p->meta()["documentation"] << "\n"; o << " \n"; o << " \n"; o << " \n"; o << " Get state of " << p->meta()["documentation"] << "\n"; o << " \n"; o << " \n"; o << " Value of " << p->meta()["documentation"] << "\n"; o << " "; o << " \n"; o << " \n"; o << " Value of %s\n", p->meta()["documentation"]; o << " "; o << " \n"; return; } o << " \n"; o << " Set Value of " << p->meta()["documentation"] << "\n"; if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { o << " \n"; o << " \n"; o << " "; } else { o << " \n"; } o << " \n"; o << " \n"; o << " Get Value of " << p->meta()["documentation"] << "\n"; o << " \n"; o << " \n"; o << " Value of " << p->meta()["documentation"] << "\n"; if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { o << " \n"; o << " \n"; o << " \n"; } else { o << " \n"; } o << " \n"; }// else if(meta.find("documentation") != meta.end()) // fprintf(stderr, "Skipping \"%s\"\n", name); //else // fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name); } std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter) { o << "\n"; o << "\n"; o << " \n"; o << " " << formatter.prog_name << "\n"; o << " " << formatter.uri << "\n"; o << " " << formatter.doc_origin << "\n"; o << " " << formatter.author_first; o << "" << formatter.author_last << "\n"; o << " \n"; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); walk_ports2(formatter.p, buffer, 1024, &o, dump_ports_cb); o << "\n"; return o; }