|
- #include "../ports.h"
- #include "../rtosc.h"
- #include "../pretty-format.h"
-
- #include <cinttypes>
- #include <ostream>
- #include <cassert>
- #include <limits>
- #include <cstring>
- #include <string>
-
- /* Compatibility with non-clang compilers */
- #ifndef __has_feature
- # define __has_feature(x) 0
- #endif
- #ifndef __has_extension
- # define __has_extension __has_feature
- #endif
-
- /* Check for C++11 support */
- #if defined(HAVE_CPP11_SUPPORT)
- # if HAVE_CPP11_SUPPORT
- # define DISTRHO_PROPER_CPP11_SUPPORT
- # endif
- #elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept)
- # define DISTRHO_PROPER_CPP11_SUPPORT
- # if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407 && ! defined(__clang__)) || (defined(__clang__) && ! __has_extension(cxx_override_control))
- # define override // gcc4.7+ only
- # define final // gcc4.7+ only
- # endif
- #endif
-
- using namespace rtosc;
-
- static inline void scat(char *dest, const char *src)
- {
- while(*dest) dest++;
- while(*src && *src!=':') *dest++ = *src++;
- *dest = 0;
- }
-
- RtData::RtData(void)
- :loc(NULL), loc_size(0), obj(NULL), matches(0), message(NULL)
- {
- for(int i=0; i<(int)(sizeof(idx)/sizeof(int)); ++i)
- idx[i] = 0;
- }
-
- void RtData::push_index(int ind)
- {
- for(int i=1; i<(int)(sizeof(idx)/sizeof(int)); ++i)
- idx[i] = idx[i-1];
- idx[0] = ind;
- }
-
- void RtData::pop_index(void)
- {
- int n = sizeof(idx)/sizeof(int);
- for(int i=n-2; i >= 0; --i)
- idx[i] = idx[i+1];
- idx[n-1] = 0;
- }
-
- void RtData::replyArray(const char *path, const char *args,
- rtosc_arg_t *vals)
- {
- (void) path;
- (void) args;
- (void) vals;
- }
- 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::chain(const char *path, const char *args, ...)
- {
- (void) path;
- (void) args;
- }
-
- void RtData::chain(const char *msg)
- {
- (void) msg;
- }
- void RtData::chainArray(const char *path, const char *args,
- rtosc_arg_t *vals)
- {
- (void) path;
- (void) args;
- (void) vals;
- };
- 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 RtData::broadcastArray(const char *path, const char *args,
- rtosc_arg_t *vals)
- {
- (void) path;
- (void) args;
- (void) vals;
- }
-
- void RtData::forward(const char *rational)
- {
- (void) rational;
- }
-
- 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::MetaIterator::operator bool(void) const
- {
- return title;
- }
-
- 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<std::string> words_t;
- typedef std::vector<std::string> svec_t;
- typedef std::vector<const char *> cvec_t;
- typedef std::vector<int> ivec_t;
- typedef std::vector<int> tuple_t;
- typedef std::vector<tuple_t> 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 no 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<class T>
- int count_dups(std::vector<T> &t)
- {
- int dups = 0;
- int N = t.size();
- bool mark[t.size()];
- memset(mark, 0, N);
- for(int i=0; i<N; ++i) {
- if(mark[i])
- continue;
- for(int j=i+1; j<N; ++j) {
- if(t[i] == t[j]) {
- dups++;
- mark[j] = true;
- }
- }
- }
- return dups;
- }
-
- template<class T, class Z>
- bool has(T &t, Z&z)
- {
- for(auto tt:t)
- if(tt==z)
- return true;
- return false;
- }
-
- static int int_max(int a, int b) { return a<b?b:a;}
-
- static ivec_t find_pos(words_t &strs)
- {
- ivec_t pos;
- int current_dups = strs.size();
- int N = 0;
- for(auto w:strs)
- N = int_max(N,w.length());
-
- int pos_best = -1;
- int pos_best_val = std::numeric_limits<int>::max();
- while(true)
- {
- for(int i=0; i<N; ++i) {
- ivec_t npos = pos;
- if(has(pos, i))
- continue;
- npos.push_back(i);
- auto hashed = do_hash(strs, npos);
- int d = count_dups(hashed);
- if(d < pos_best_val) {
- pos_best_val = d;
- pos_best = i;
- }
- }
- if(pos_best_val >= 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;
- }
-
- static 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;
- }
-
- static 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<char> 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<N; ++i)
- assoc.push_back(0);
-
- int assoc_best = -1;
- int assoc_best_val = std::numeric_limits<int>::max();;
- for(int k=0; k<4; ++k)
- {
- for(int i:useful_chars) {
- assoc_best_val = std::numeric_limits<int>::max();
- for(int j=0; j<100; ++j) {
- //printf(".");
- assoc[i] = j;
- auto hashed = do_hash(strs, pos, assoc);
- //for(int i=0; i<hashed.size(); ++i)
- // printf("%d ", hashed[i]);
- //printf("\n");
- int d = count_dups(hashed);
- //printf("dup %d\n",d);
- if(d < assoc_best_val) {
- assoc_best_val = d;
- assoc_best = j;
- }
- }
- assoc[i] = assoc_best;
- }
- if(assoc_best_val >= 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;
- }
-
- static 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<strs.size(); ++i)
- // printf("%d) '%s'\n", hashed[i], strs[i].c_str());
- int N = 0;
- for(auto h:hashed)
- N = int_max(N,h+1);
- for(int i=0; i<N; ++i)
- remap.push_back(0);
- for(int i=0; i<(int)hashed.size(); ++i)
- remap[hashed[i]] = i;
-
- return remap;
- }
-
- static void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm)
- {
- if(str.empty())
- return;
- 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);
- }
-
- static void generate_minimal_hash(Ports &p, Port_Matcher &pm)
- {
- svec_t keys;
- cvec_t args;
-
- bool enump = false;
- for(unsigned i=0; i<p.ports.size(); ++i)
- if(strchr(p.ports[i].name, '#'))
- enump = true;
- if(enump)
- return;
- for(unsigned i=0; i<p.ports.size(); ++i)
- {
- std::string tmp = p.ports[i].name;
- const char *arg = NULL;
- int idx = tmp.find(':');
- if(idx > 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<Port> l)
- :ports(l), impl(NULL)
- {
- refreshMagic();
- }
-
- 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, bool base_dispatch) const
- {
- if(!strcmp(m, "pointer"))
- {
- // rRecur*Cb have already set d.loc to the pointer we need,
- // so we just return
- return;
- }
-
- void *obj = d.obj;
-
- //handle the first dispatch layer
- if(base_dispatch) {
- d.matches = 0;
- d.message = m;
- if(m && *m == '/')
- m++;
- if(d.loc)
- d.loc[0] = 0;
- }
-
- //simple case
- if(!d.loc || !d.loc_size) {
- for(const Port &port: ports) {
- if(rtosc_match(port.name,m, NULL))
- 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; i<elms; ++i) {
- const Port &port = ports[i];
- const char* m_end;
- if(!rtosc_match(port.name, m, &m_end))
- continue;
- if(!port.ports)
- d.matches++;
-
- //Append the path
- if(strchr(port.name,'#')) {
- const char *msg = m;
- char *pos = old_end;
- while(*msg && msg != m_end)
- *pos++ = *msg++;
- *pos = '\0';
- } else
- scat(d.loc, port.name);
-
- d.port = &port;
-
- //Apply callback
- port.cb(m,d), d.obj = obj;
-
- //Remove the rest of the path
- char *tmp = old_end;
- while(*tmp) *tmp++=0;
- }
- } else {
-
- //Define string to be hashed
- unsigned len=0;
- const char *tmp = m;
-
- while(*tmp && *tmp != '/')
- tmp++;
- if(*tmp == '/')
- tmp++;
- len = tmp-m;
-
- //Compute the hash
- int t = len;
- for(auto p:impl->pos)
- if(p < (int)len)
- t += impl->assoc[m[p]];
- if(t >= (int)impl->remap.size() && !default_handler)
- return;
- else if(t >= (int)impl->remap.size() && default_handler) {
- d.matches++;
- default_handler(m,d), d.obj = obj;
- 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';
- } else if(default_handler) {
- d.matches++;
- default_handler(m,d), d.obj = obj;
- }
- }
- }
- }
-
- /*
- * Returning values from runtime
- */
-
- //! RtData subclass to capture argument values pretty-printed from
- //! a runtime object
- class CapturePretty : public RtData
- {
- char* buffer;
- std::size_t buffersize;
- int cols_used;
-
- void reply(const char *) override { assert(false); }
-
- /* void replyArray(const char*, const char *args,
- rtosc_arg_t *vals)
- {
- size_t cur_idx = 0;
- for(const char* ptr = args; *ptr; ++ptr, ++cur_idx)
- {
- assert(cur_idx < max_args);
- arg_vals[cur_idx].type = *ptr;
- arg_vals[cur_idx].val = vals[cur_idx];
- }
-
- // TODO: refactor code, also with Capture ?
- size_t wrt = rtosc_print_arg_vals(arg_vals, cur_idx,
- buffer, buffersize, NULL,
- cols_used);
- assert(wrt);
- }*/
-
- void reply(const char *, const char *args, ...) override
- {
- va_list va;
- va_start(va,args);
-
- size_t nargs = strlen(args);
- rtosc_arg_val_t arg_vals[nargs];
-
- rtosc_v2argvals(arg_vals, nargs, args, va);
-
- size_t wrt = rtosc_print_arg_vals(arg_vals, nargs,
- buffer, buffersize, NULL,
- cols_used);
- (void) wrt;
- va_end(va);
- assert(wrt);
- }
-
- public:
- //! Return the argument values, pretty-printed
- const char* value() const { return buffer; }
- CapturePretty(char* buffer, std::size_t size, int cols_used) :
- buffer(buffer), buffersize(size), cols_used(cols_used) {}
- };
-
- /**
- * @brief Returns a port's value pretty-printed from a runtime object. The
- * port object must not be known.
- *
- * For the parameters, see the overloaded function
- * @return The argument values, pretty-printed
- */
- static const char* get_value_from_runtime(void* runtime,
- const Ports& ports,
- size_t loc_size,
- char* loc,
- char* buffer_with_port,
- std::size_t buffersize,
- int cols_used)
- {
- std::size_t addr_len = strlen(buffer_with_port);
-
- // use the port buffer to print the result, but do not overwrite the
- // port name
- CapturePretty d(buffer_with_port + addr_len, buffersize - addr_len,
- cols_used);
- d.obj = runtime;
- d.loc_size = loc_size;
- d.loc = loc;
- d.matches = 0;
-
- // does the message at least fit the arguments?
- assert(buffersize - addr_len >= 8);
- // append type
- memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments
- buffer_with_port[addr_len + (4-addr_len%4)] = ',';
-
- d.message = buffer_with_port;
-
- // buffer_with_port is a message in this call:
- ports.dispatch(buffer_with_port, d, false);
-
- return d.value();
- }
-
- //! RtData subclass to capture argument values from a runtime object
- class Capture : public RtData
- {
- size_t max_args;
- rtosc_arg_val_t* arg_vals;
- int nargs;
-
- void chain(const char *path, const char *args, ...) override
- {
- nargs = 0;
- }
-
- void chain(const char *msg) override
- {
- nargs = 0;
- }
-
- void reply(const char *) override { assert(false); }
-
- void replyArray(const char*, const char *args,
- rtosc_arg_t *vals) override
- {
- size_t cur_idx = 0;
- for(const char* ptr = args; *ptr; ++ptr, ++cur_idx)
- {
- assert(cur_idx < max_args);
- arg_vals[cur_idx].type = *ptr;
- arg_vals[cur_idx].val = vals[cur_idx];
- }
- nargs = cur_idx;
- }
-
- void reply(const char *, const char *args, ...) override
- {
- va_list va;
- va_start(va,args);
-
- nargs = strlen(args);
- assert((size_t)nargs <= max_args);
-
- rtosc_v2argvals(arg_vals, nargs, args, va);
-
- va_end(va);
- }
- public:
- //! Return the number of argument values stored
- int size() const { return nargs; }
- Capture(std::size_t max_args, rtosc_arg_val_t* arg_vals) :
- max_args(max_args), arg_vals(arg_vals), nargs(-1) {}
- };
-
- /**
- * @brief Returns a port's current value(s)
- *
- * This function returns the value(s) of a known port object and stores them as
- * rtosc_arg_val_t.
- * @param runtime The runtime object
- * @param port the port where the value shall be retrieved
- * @param loc A buffer where dispatch can write down the currently dispatched
- * path
- * @param loc_size Size of loc
- * @param portname_from_base The name of the port, relative to its base
- * @param buffer_with_port A buffer which already contains the port.
- * This buffer will be modified and must at least have space for 8 more bytes.
- * @param buffersize Size of @p buffer_with_port
- * @param max_args Maximum capacity of @p arg_vals
- * @param arg_vals Argument buffer for returned argument values
- * @return The number of argument values stored in @p arg_vals
- */
- static size_t get_value_from_runtime(void* runtime,
- const Port& port,
- size_t loc_size,
- char* loc,
- const char* portname_from_base,
- char* buffer_with_port,
- std::size_t buffersize,
- std::size_t max_args,
- rtosc_arg_val_t* arg_vals)
- {
- strncpy(buffer_with_port, portname_from_base, buffersize);
- std::size_t addr_len = strlen(buffer_with_port);
-
- Capture d(max_args, arg_vals);
- d.obj = runtime;
- d.loc_size = loc_size;
- d.loc = loc;
- d.port = &port;
- d.matches = 0;
- assert(*loc);
-
- // does the message at least fit the arguments?
- assert(buffersize - addr_len >= 8);
- // append type
- memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments
- buffer_with_port[addr_len + (4-addr_len%4)] = ',';
-
- // TODO? code duplication
-
- // buffer_with_port is a message in this call:
- d.message = buffer_with_port;
- port.cb(buffer_with_port, d);
-
- assert(d.size() >= 0);
- return d.size();
- }
-
- /*
- * default values
- */
-
- const char* rtosc::get_default_value(const char* port_name, const Ports& ports,
- void* runtime, const Port* port_hint,
- int32_t idx, int recursive)
- {
- constexpr std::size_t buffersize = 1024;
- char buffer[buffersize];
- char loc[buffersize] = "";
-
- assert(recursive >= 0); // forbid recursing twice
-
- char default_annotation[20] = "default";
- // if(idx > 0)
- // snprintf(default_annotation + 7, 13, "[%" PRId32 "]", idx);
- const char* const dependent_annotation = "default depends";
- const char* return_value = nullptr;
-
- if(!port_hint)
- port_hint = ports.apropos(port_name);
- assert(port_hint); // port must be found
- const Port::MetaContainer metadata = port_hint->meta();
-
- // Let complex cases depend upon a marker variable
- // If the runtime is available the exact preset number can be found
- // This generalizes to envelope types nicely if envelopes have a read
- // only port which indicates if they're amplitude/frequency/etc
- const char* dependent = metadata[dependent_annotation];
- if(dependent)
- {
- char* dependent_port = buffer;
- *dependent_port = 0;
-
- assert(strlen(port_name) + strlen(dependent_port) + 5 < buffersize);
- strncat(dependent_port, port_name,
- buffersize - strlen(dependent_port) - 1);
- strncat(dependent_port, "/../",
- buffersize - strlen(dependent_port) - 1);
- strncat(dependent_port, dependent,
- buffersize - strlen(dependent_port) - 1);
- dependent_port = Ports::collapsePath(dependent_port);
-
- // TODO: collapsePath bug?
- // Relative paths should not start with a slash after collapsing ...
- if(*dependent_port == '/')
- ++dependent_port;
-
- const char* dependent_value =
- runtime
- ? get_value_from_runtime(runtime, ports,
- buffersize, loc,
- dependent_port,
- buffersize-1, 0)
- : get_default_value(dependent_port, ports,
- runtime, NULL, recursive-1);
-
- assert(strlen(dependent_value) < 16); // must be an int
-
- char* default_variant = buffer;
- *default_variant = 0;
- assert(strlen(default_annotation) + 1 + 16 < buffersize);
- strncat(default_variant, default_annotation,
- buffersize - strlen(default_variant));
- strncat(default_variant, " ", buffersize - strlen(default_variant));
- strncat(default_variant, dependent_value,
- buffersize - strlen(default_variant));
-
- return_value = metadata[default_variant];
- }
-
- // If return_value is NULL, this can have two meanings:
- // 1. there was no depedent annotation
- // => check for a direct (non-dependent) default value
- // (a non existing direct default value is OK)
- // 2. there was a dependent annotation, but the dependent value has no
- // mapping (mapping for default_variant was NULL)
- // => check for the direct default value, which acts as a default
- // mapping for all presets; a missing default value indicates an
- // error in the metadata
- if(!return_value)
- {
- return_value = metadata[default_annotation];
- assert(!dependent || return_value);
- }
-
- return return_value;
- }
-
- int rtosc::canonicalize_arg_vals(rtosc_arg_val_t* av, size_t n,
- const char* port_args,
- Port::MetaContainer meta)
- {
- const char* first = port_args;
- int errors_found = 0;
-
- for( ; *first && (*first == ':' || *first == '[' || *first == ']');
- ++first) ;
-
- for(size_t i = 0; i < n; ++i, ++first, ++av)
- {
- for( ; *first && (*first == '[' || *first == ']'); ++first) ;
-
- if(!*first || *first == ':')
- {
- // (n-i) arguments left, but we have no recipe to convert them
- return n-i;
- }
-
- if(av->type == 'S' && *first == 'i')
- {
- int val = enum_key(meta, av->val.s);
- if(val == std::numeric_limits<int>::min())
- ++errors_found;
- else
- {
- av->type = 'i';
- av->val.i = val;
- }
- }
- }
- return errors_found;
- }
-
- void rtosc::map_arg_vals(rtosc_arg_val_t* av, size_t n,
- Port::MetaContainer meta)
- {
- char mapbuf[20] = "map ";
-
- for(size_t i = 0; i < n; ++i, ++av)
- {
- if(av->type == 'i')
- {
- snprintf(mapbuf + 4, 16, "%d", av->val.i);
- const char* val = meta[mapbuf];
- if(val)
- {
- av->type = 'S';
- av->val.s = val;
- }
- }
- }
- }
-
- int rtosc::get_default_value(const char* port_name, const char* port_args,
- const Ports& ports,
- void* runtime, const Port* port_hint,
- int32_t idx,
- size_t n, rtosc_arg_val_t* res,
- char* strbuf, size_t strbufsize)
- {
- const char* pretty = get_default_value(port_name, ports, runtime, port_hint,
- idx, 0);
-
- int nargs;
- if(pretty)
- {
- nargs = rtosc_count_printed_arg_vals(pretty);
- assert(nargs > 0); // parse error => error in the metadata?
- assert((size_t)nargs < n);
-
- rtosc_scan_arg_vals(pretty, res, nargs, strbuf, strbufsize);
-
- {
- int errs_found = canonicalize_arg_vals(res,
- nargs,
- port_args,
- port_hint->meta());
- if(errs_found)
- fprintf(stderr, "Could not canonicalize %s\n", pretty);
- assert(!errs_found); // error in the metadata?
- }
- }
- else
- nargs = -1;
-
- return nargs;
- }
-
- std::string rtosc::get_changed_values(const Ports& ports, void* runtime)
- {
- std::string res;
- constexpr std::size_t buffersize = 1024;
- char port_buffer[buffersize];
- memset(port_buffer, 0, buffersize); // requirement for walk_ports
-
- const size_t max_arg_vals = 256;
-
- auto on_reach_port =
- [](const Port* p, const char* port_buffer,
- const char* port_from_base, const Ports& base,
- void* data, void* runtime)
- {
- assert(runtime);
- const Port::MetaContainer meta = p->meta();
-
- if((p->name[strlen(p->name)-1] != ':' && !strstr(p->name, "::"))
- || meta.find("parameter") == meta.end())
- {
- // runtime information can not be retrieved,
- // thus, it can not be compared with the default value
- return;
- }
-
- char loc[buffersize] = "";
- rtosc_arg_val_t arg_vals_default[max_arg_vals];
- rtosc_arg_val_t arg_vals_runtime[max_arg_vals];
- char buffer_with_port[buffersize];
- char cur_value_pretty[buffersize] = " ";
- char strbuf[buffersize]; // temporary string buffer for pretty-printing
-
- std::string* res = (std::string*)data;
- assert(strlen(port_buffer) + 1 < buffersize);
- strncpy(loc, port_buffer, buffersize); // TODO: +-1?
-
- strncpy(buffer_with_port, port_from_base, buffersize);
- const char* portargs = strchr(p->name, ':');
- if(!portargs)
- portargs = p->name + strlen(p->name);
-
- #if 0 // debugging stuff
- if(!strncmp(port_buffer, "/part1/Penabled", 5) &&
- !strncmp(port_buffer+6, "/Penabled", 9))
- {
- printf("runtime: %ld\n", (long int)runtime);
- }
- #endif
- // TODO: p->name: duplicate to p
- int nargs_default = get_default_value(p->name,
- portargs,
- base,
- runtime,
- p,
- -1,
- max_arg_vals,
- arg_vals_default,
- strbuf,
- buffersize);
- size_t nargs_runtime = get_value_from_runtime(runtime,
- *p,
- buffersize, loc,
- port_from_base,
- buffer_with_port,
- buffersize,
- max_arg_vals,
- arg_vals_runtime);
-
- if(nargs_default == (int) nargs_runtime)
- {
- canonicalize_arg_vals(arg_vals_default, nargs_default,
- strchr(p->name, ':'), meta);
- if(!rtosc_arg_vals_eq(arg_vals_default,
- arg_vals_runtime,
- nargs_default,
- nargs_runtime,
- NULL))
- {
- map_arg_vals(arg_vals_runtime, nargs_runtime, meta);
- rtosc_print_arg_vals(arg_vals_runtime, nargs_runtime,
- cur_value_pretty + 1, buffersize - 1,
- NULL, strlen(port_buffer) + 1);
-
- *res += port_buffer;
- *res += cur_value_pretty;
- *res += "\n";
- }
- }
- };
-
- walk_ports(&ports, port_buffer, buffersize, &res, on_reach_port,
- runtime);
-
- if(res.length()) // remove trailing newline
- res.resize(res.length()-1);
- return res;
- }
-
- void rtosc::savefile_dispatcher_t::operator()(const char* msg)
- {
- *loc = 0;
- RtData d;
- d.obj = runtime;
- d.loc = loc; // we're always dispatching at the base
- d.loc_size = 1024;
- ports->dispatch(msg, d, true);
- }
-
- int savefile_dispatcher_t::default_response(size_t nargs,
- bool first_round,
- savefile_dispatcher_t::dependency_t
- dependency)
- {
- // default implementation:
- // no dependencies => round 0,
- // has dependencies => round 1,
- // not specified => both rounds
- return (dependency == not_specified
- || !(dependency ^ first_round))
- ? nargs // argument number is not changed
- : (int)discard;
- }
-
- int savefile_dispatcher_t::on_dispatch(size_t, char *,
- size_t, size_t nargs,
- rtosc_arg_val_t *,
- bool round2,
- dependency_t dependency)
- {
- return default_response(nargs, round2, dependency);
- }
-
- int rtosc::dispatch_printed_messages(const char* messages,
- const Ports& ports, void* runtime,
- savefile_dispatcher_t* dispatcher)
- {
- constexpr std::size_t buffersize = 1024;
- char portname[buffersize], message[buffersize], strbuf[buffersize];
- int rd, rd_total = 0;
- int nargs;
- int msgs_read = 0;
-
- savefile_dispatcher_t dummy_dispatcher;
- if(!dispatcher)
- dispatcher = &dummy_dispatcher;
- dispatcher->ports = &ports;
- dispatcher->runtime = runtime;
-
- // scan all messages twice:
- // * in the second round, only dispatch those with ports that depend on
- // other ports
- // * in the first round, only dispatch all others
- for(int round = 0; round < 2 && msgs_read >= 0; ++round)
- {
- msgs_read = 0;
- rd_total = 0;
- const char* msg_ptr = messages;
- while(*msg_ptr && (msgs_read >= 0))
- {
- nargs = rtosc_count_printed_arg_vals_of_msg(msg_ptr);
- if(nargs >= 0)
- {
- // 16 is usually too much, but it allows the user to add
- // arguments if necessary
- size_t maxargs = 16;
- rtosc_arg_val_t arg_vals[maxargs];
- rd = rtosc_scan_message(msg_ptr, portname, buffersize,
- arg_vals, nargs, strbuf, buffersize);
- rd_total += rd;
-
- const Port* port = ports.apropos(portname);
- savefile_dispatcher_t::dependency_t dependency =
- (savefile_dispatcher_t::dependency_t)
- (port
- ? !!port->meta()["default depends"]
- : (int)savefile_dispatcher_t::not_specified);
-
- // let the user modify the message and the args
- // the argument number may have changed, or the user
- // wants to discard the message or abort the savefile loading
- nargs = dispatcher->on_dispatch(buffersize, portname,
- maxargs, nargs, arg_vals,
- round, dependency);
-
- if(nargs == savefile_dispatcher_t::abort)
- msgs_read = -rd_total-1; // => causes abort
- else
- {
- if(nargs != savefile_dispatcher_t::discard)
- {
- rtosc_arg_t vals[nargs];
- char argstr[nargs+1];
- for(int i = 0; i < nargs; ++i) {
- vals[i] = arg_vals[i].val;
- argstr[i] = arg_vals[i].type;
- }
- argstr[nargs] = 0;
-
- rtosc_amessage(message, buffersize, portname,
- argstr, vals);
-
- (*dispatcher)(message);
- }
- }
-
- msg_ptr += rd;
- ++msgs_read;
- }
- else if(nargs == std::numeric_limits<int>::min())
- {
- // this means the (rest of the) file is whitespace only
- // => don't increase msgs_read
- while(*++msg_ptr) ;
- }
- else {
- // overwrite meaning of msgs_read in order to
- // inform the user where the read error occurred
- msgs_read = -rd_total-1;
- }
- }
- }
- return msgs_read;
- }
-
- std::string rtosc::save_to_file(const Ports &ports, void *runtime,
- const char *appname, rtosc_version appver)
- {
- std::string res;
- char rtosc_vbuf[12], app_vbuf[12];
-
- {
- rtosc_version rtoscver = rtosc_current_version();
- rtosc_version_print_to_12byte_str(&rtoscver, rtosc_vbuf);
- rtosc_version_print_to_12byte_str(&appver, app_vbuf);
- }
-
- res += "% RT OSC v"; res += rtosc_vbuf; res += " savefile\n"
- "% "; res += appname; res += " v"; res += app_vbuf; res += "\n";
- res += get_changed_values(ports, runtime);
-
- return res;
- }
-
- int rtosc::load_from_file(const char* file_content,
- const Ports& ports, void* runtime,
- const char* appname,
- rtosc_version appver,
- savefile_dispatcher_t* dispatcher)
- {
- char appbuf[128];
- int bytes_read = 0;
-
- if(dispatcher)
- {
- dispatcher->app_curver = appver;
- dispatcher->rtosc_curver = rtosc_current_version();
- }
-
- unsigned vma, vmi, vre;
- int n = 0;
-
- sscanf(file_content,
- "%% RT OSC v%u.%u.%u savefile%n ", &vma, &vmi, &vre, &n);
- if(n <= 0 || vma > 255 || vmi > 255 || vre > 255)
- return -bytes_read-1;
- if(dispatcher)
- {
- dispatcher->rtosc_filever.major = vma;
- dispatcher->rtosc_filever.minor = vmi;
- dispatcher->rtosc_filever.revision = vre;
- }
- file_content += n;
- bytes_read += n;
- n = 0;
-
- sscanf(file_content,
- "%% %128s v%u.%u.%u%n ", appbuf, &vma, &vmi, &vre, &n);
- if(n <= 0 || strcmp(appbuf, appname) || vma > 255 || vmi > 255 || vre > 255)
- return -bytes_read-1;
-
- if(dispatcher)
- {
- dispatcher->app_filever.major = vma;
- dispatcher->app_filever.minor = vmi;
- dispatcher->app_filever.revision = vre;
- }
- file_content += n;
- bytes_read += n;
- n = 0;
-
- int rval = dispatch_printed_messages(file_content,
- ports, runtime, dispatcher);
- return (rval < 0) ? (rval-bytes_read) : rval;
- }
-
- /*
- * Miscellaneous
- */
-
- 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, NULL))
- 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 ||
- rtosc_match_path(port.name, path, NULL)))
- return &port;
-
- return NULL;
- }
-
- static bool parent_path_p(char *read, char *start)
- {
- if(read-start<2)
- return false;
- return read[0]=='.' && read[-1]=='.' && read[-2]=='/';
- }
-
- static void read_path(char *&r, char *start)
- {
- while(1)
- {
- if(r<start)
- break;
- bool doBreak = *r=='/';
- r--;
- if(doBreak)
- break;
- }
- }
-
- static void move_path(char *&r, char *&w, char *start)
- {
- while(1)
- {
- if(r<start)
- break;
- bool doBreak = *r=='/';
- *w-- = *r--;
- if(doBreak)
- break;
- }
- }
-
-
- char *Ports::collapsePath(char *p)
- {
- //obtain the pointer to the last non-null char
- char *p_end = p;
- while(*p_end) p_end++;
- p_end--;
-
- //number of subpaths to consume
- int consuming = 0;
-
- char *write_pos = p_end;
- char *read_pos = p_end;
- while(read_pos >= p) {
- //per path chunk either
- //(1) find a parent ref and inc consuming
- //(2) find a normal ref and consume
- //(3) find a normal ref and write through
- bool ppath = parent_path_p(read_pos, p);
- if(ppath) {
- read_path(read_pos, p);
- consuming++;
- } else if(consuming) {
- read_path(read_pos, p);
- consuming--;
- } else
- move_path(read_pos, write_pos, p);
- }
- //return last written location, not next to write
- return write_pos+1;
- };
-
- void Ports::refreshMagic()
- {
- delete impl;
- 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();
- }
-
- ClonePorts::ClonePorts(const Ports &ports_,
- std::initializer_list<ClonePort> c)
- :Ports({})
- {
- for(auto &to_clone:c) {
- const Port *clone_port = NULL;
- for(auto &p:ports_.ports)
- if(!strcmp(p.name, to_clone.name))
- clone_port = &p;
- if(!clone_port && strcmp("*", to_clone.name)) {
- fprintf(stderr, "Cannot find a clone port for '%s'\n",to_clone.name);
- assert(false);
- }
-
- if(clone_port) {
- ports.push_back({clone_port->name, clone_port->metadata,
- clone_port->ports, to_clone.cb});
- } else {
- default_handler = to_clone.cb;
- }
- }
-
- refreshMagic();
- }
- MergePorts::MergePorts(std::initializer_list<const rtosc::Ports*> c)
- :Ports({})
- {
- //XXX TODO remove duplicates in some sane and documented way
- //e.g. repeated ports override and remove older ones
- for(auto *to_clone:c) {
- assert(to_clone);
- for(auto &p:to_clone->ports) {
- bool already_there = false;
- for(auto &pp:ports)
- if(!strcmp(pp.name, p.name))
- already_there = true;
-
- if(!already_there)
- ports.push_back(p);
- }
- }
-
- refreshMagic();
- }
-
- /**
- * @brief Check if the port @p port is enabled
- * @param port The port to be checked. Usually of type rRecur* or rSelf.
- * @param loc The absolute path of @p port
- * @param loc_size The maximum usable size of @p loc
- * @param ports The Ports object containing @p port
- * @param runtime TODO
- * @return TODO
- */
- bool port_is_enabled(const Port* port, char* loc, size_t loc_size,
- const Ports& base, void *runtime)
- {
- if(port && runtime)
- {
- const char* enable_port = port->meta()["enabled by"];
- if(enable_port)
- {
- /*
- find out which Ports object to dispatch at
- (the current one or its child?)
- */
- const char* n = port->name;
- const char* e = enable_port;
- for( ; *n && (*n == *e) && *n != '/' && *e != '/'; ++n, ++e) ;
-
- bool subport = (*e == '/' && *n == '/');
-
- const char* ask_port_str = subport
- ? e+1
- : enable_port;
-
- const Ports& ask_ports = subport ? *base[port->name]->ports
- : base;
-
- assert(!strchr(ask_port_str, '/'));
- const Port* ask_port = ask_ports[ask_port_str];
- assert(ask_port);
-
- rtosc_arg_val_t rval;
-
- /*
- concatenate the location string
- */
- if(subport)
- strncat(loc, "/../", loc_size - strlen(loc) - 1);
- strncat(loc, enable_port, loc_size - strlen(loc) - 1);
-
- char* collapsed_loc = Ports::collapsePath(loc);
- loc_size -= (collapsed_loc - loc);
-
- // TODO: collapse, use .. only in one case
- /*
- receive the "enabled" property
- */
- char buf[loc_size];
- #ifdef NEW_CODE
- strncpy(buf, collapsed_loc, loc_size);
- #else
- // TODO: try to use portname_from_base, since Ports might
- // also be of type a#N/b
- const char* last_slash = strrchr(collapsed_loc, '/');
- strncpy(buf,
- last_slash ? last_slash + 1 : collapsed_loc,
- loc_size);
- #endif
- get_value_from_runtime(runtime, *ask_port,
- loc_size, collapsed_loc, ask_port_str,
- buf, 0, 1, &rval);
- assert(rval.type == 'T' || rval.type == 'F');
- return rval.val.T == 'T';
- }
- else // Port has no "enabled" property, so it is always enabled
- return true;
- }
- else // no runtime provided, so run statically through all subports
- return true;
- }
-
- // TODO: copy the changes into walk_ports_2
- void rtosc::walk_ports(const Ports *base,
- char *name_buffer,
- size_t buffer_size,
- void *data,
- port_walker_t walker,
- void* runtime)
- {
- auto walk_ports_recurse = [](const Port& p, char* name_buffer,
- size_t buffer_size, const Ports& base,
- void* data, port_walker_t walker,
- void* runtime, const char* old_end)
- {
- // TODO: all/most of these checks must also be done for the
- // first, non-recursive call
- bool enabled = true;
- if(runtime)
- {
- enabled = (p.meta().find("no walk") == p.meta().end());
- if(enabled)
- {
- // get child runtime and check if it's NULL
- RtData r;
- r.obj = runtime;
- r.port = &p;
-
- char buf[1024];
- strncpy(buf, old_end, 1024);
- strncat(buf, "pointer", 1024 - strlen(buf) - 1);
- assert(1024 - strlen(buf) >= 8);
- strncpy(buf + strlen(buf) + 1, ",", 2);
-
- p.cb(buf, r);
- runtime = r.obj; // callback has stored the child pointer here
- // if there is runtime information, but the pointer is NULL,
- // the port is not enabled
- enabled = (bool) runtime;
- if(enabled)
- {
- // check if the port is disabled by a switch
- enabled = port_is_enabled(&p, name_buffer, buffer_size,
- base, runtime);
- }
- }
- }
- if(enabled)
- rtosc::walk_ports(p.ports, name_buffer, buffer_size,
- data, walker, runtime);
- };
-
- //only walk valid ports
- if(!base)
- return;
-
- 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;
-
- if(port_is_enabled((*base)["self:"], name_buffer, buffer_size, *base,
- runtime))
- for(const Port &p: *base) {
- //if(strchr(p.name, '/')) {//it is another tree
- if(p.ports) {//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; i<max; ++i)
- {
- sprintf(pos,"%d",i);
-
- //Ensure the result is a path
- if(strrchr(name_buffer, '/')[1] != '/')
- strcat(name_buffer, "/");
-
- //Recurse
- walk_ports_recurse(p, name_buffer, buffer_size,
- *base, data, walker, runtime, old_end);
- }
- } else {
- //Append the path
- const char* old_end = name_buffer + strlen(name_buffer);
- scat(name_buffer, p.name);
-
- //Recurse
- walk_ports_recurse(p, name_buffer, buffer_size,
- *base, data, walker, runtime, old_end);
- }
- } else {
- if(strchr(p.name,'#')) {
- const char *name = p.name;
- char *pos = old_end;
- while(*name != '#') *pos++ = *name++;
- const unsigned max = atoi(name+1);
- while(isdigit(*++name)) ;
-
- for(unsigned i=0; i<max; ++i)
- {
- char* pos_after_num = pos + sprintf(pos,"%d",i);
- const char* name2_2 = name;
-
- // append everything behind the '#' (for cases like a#N/b)
- while(*name2_2 && *name2_2 != ':')
- *pos_after_num++ = *name2_2++;
-
- //Apply walker function
- walker(&p, name_buffer, old_end, *base, data, runtime);
- }
- } else {
- //Append the path
- scat(name_buffer, p.name);
-
- //Apply walker function
- walker(&p, name_buffer, old_end, *base, data, runtime);
- }
- }
-
- //Remove the rest of the path
- char *tmp = old_end;
- while(*tmp) *tmp++=0;
- }
- }
-
- void walk_ports2(const rtosc::Ports *base,
- char *name_buffer,
- size_t buffer_size,
- void *data,
- rtosc::port_walker_t walker)
- {
- if(!base)
- return;
-
- 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 rtosc::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; i<max; ++i)
- {
- sprintf(pos,"[0,%d]",max-1);
-
- //Ensure the result is a path
- if(strrchr(name_buffer, '/')[1] != '/')
- strcat(name_buffer, "/");
-
- //Recurse
- walk_ports2(p.ports, name_buffer, buffer_size,
- data, walker);
- }
- } else {
- //Append the path
- scat(name_buffer, p.name);
-
- //Recurse
- walk_ports2(p.ports, name_buffer, buffer_size,
- data, walker);
- }
- } else {
- 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; i<max; ++i)
- {
- sprintf(pos,"[0,%d]",max-1);
-
- //Apply walker function
- walker(&p, name_buffer, old_end, *base, data, nullptr);
- }
- } else {
- //Append the path
- scat(name_buffer, p.name);
-
- //Apply walker function
- walker(&p, name_buffer, old_end, *base, data, nullptr);
- }
- }
-
- //Remove the rest of the path
- char *tmp = old_end;
- while(*tmp) *tmp++=0;
- }
- }
-
- static void units(std::ostream &o, const char *u)
- {
- if(!u)
- return;
- o << " units=\"" << u << "\"";
- }
-
- using std::ostream;
- using std::string;
- static int enum_min(Port::MetaContainer meta)
- {
- int min = 0;
- for(auto m:meta)
- if(strstr(m.title, "map "))
- min = atoi(m.title+4);
-
- for(auto m:meta)
- if(strstr(m.title, "map "))
- min = min>atoi(m.title+4) ? atoi(m.title+4) : min;
-
- return min;
- }
-
- static int enum_max(Port::MetaContainer meta)
- {
- int max = 0;
- for(auto m:meta)
- if(strstr(m.title, "map "))
- max = atoi(m.title+4);
-
- for(auto m:meta)
- if(strstr(m.title, "map "))
- max = max<atoi(m.title+4) ? atoi(m.title+4) : max;
-
- return max;
- }
-
- int rtosc::enum_key(Port::MetaContainer meta, const char* value)
- {
- int result = std::numeric_limits<int>::min();
-
- for(auto m:meta)
- if(strstr(m.title, "map "))
- if(!strcmp(m.value, value))
- {
- result = atoi(m.title+4);
- break;
- }
-
- return result;
- }
-
- static ostream &add_options(ostream &o, Port::MetaContainer meta)
- {
- string sym_names = "xyzabcdefghijklmnopqrstuvw";
- int sym_idx = 0;
- bool has_options = false;
- for(auto m:meta)
- if(strstr(m.title, "map "))
- has_options = true;
- for(auto m:meta)
- if(strcmp(m.title, "documentation") &&
- strcmp(m.title, "parameter") &&
- strcmp(m.title, "max") &&
- strcmp(m.title, "min"))
- printf("m.title = <%s>\n", m.title);
-
- if(!has_options)
- return o;
-
- o << " <hints>\n";
- for(auto m:meta) {
- if(strstr(m.title, "map ")) {
- o << " <point symbol=\"" << sym_names[sym_idx++] << "\" value=\"";
- o << m.title+4 << "\">" << m.value << "</point>\n";
- }
- }
- o << " </hints>\n";
-
- return o;
- }
- static ostream &dump_t_f_port(ostream &o, string name, string doc)
- {
- o << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n";
- o << " <desc>Enable " << doc << "</desc>\n";
- o << " <param_T symbol=\"x\"/>\n";
- o << " </message_in>\n";
- o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n";
- o << " <desc>Disable " << doc << "</desc>\n";
- o << " <param_F symbol=\"x\"/>\n";
- o << " </message_in>\n";
- o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
- o << " <desc>Get state of " << doc << "</desc>\n";
- o << " </message_in>\n";
- o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n";
- o << " <desc>Value of " << doc << "</desc>\n";
- o << " <param_T symbol=\"x\"/>";
- o << " </message_out>\n";
- o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n";
- o << " <desc>Value of " << doc << "</desc>\n";
- o << " <param_F symbol=\"x\"/>";
- o << " </message_out>\n";
- return o;
- }
- static ostream &dump_any_port(ostream &o, string name, string doc)
- {
- o << " <message_in pattern=\"" << name << "\" typetag=\"*\">\n";
- o << " <desc>" << doc << "</desc>\n";
- o << " </message_in>\n";
- return o;
- }
-
- static ostream &dump_generic_port(ostream &o, string name, string doc, string type)
- {
- const char *t = type.c_str();
- string arg_names = "xyzabcdefghijklmnopqrstuvw";
-
- //start out with argument separator
- if(*t++ != ':')
- return o;
- //now real arguments (assume [] don't exist)
- string args;
- while(*t && *t != ':')
- args += *t++;
-
- o << " <message_in pattern=\"" << name << "\" typetag=\"" << args << "\">\n";
- o << " <desc>" << doc << "</desc>\n";
-
- assert(args.length()<arg_names.length());
- for(unsigned i=0; i<args.length(); ++i)
- o << " <param_" << args[i] << " symbol=\"" << arg_names[i] << "\"/>\n";
- o << " </message_in>\n";
-
- if(*t == ':')
- return dump_generic_port(o, name, doc, t);
- else
- return o;
- }
-
- void dump_ports_cb(const rtosc::Port *p, const char *name,const char*,
- const Ports&,void *v, void*)
- {
- std::ostream &o = *(std::ostream*)v;
- auto meta = p->meta();
- const char *args = strchr(p->name, ':');
- auto mparameter = meta.find("parameter");
- auto mdoc = meta.find("documentation");
- string doc;
-
- if(mdoc != p->meta().end())
- doc = mdoc.value;
- if(meta.find("internal") != meta.end()) {
- doc += "[INTERNAL]";
- }
-
- if(mparameter != p->meta().end()) {
- char type = 0;
- if(args) {
- if(strchr(args, 'f'))
- type = 'f';
- else if(strchr(args, 'i'))
- type = 'i';
- else if(strchr(args, 'c'))
- type = 'c';
- else if(strchr(args, 'T'))
- type = 't';
- else if(strchr(args, 's'))
- type = 's';
- }
-
- if(!type) {
- fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name);
- fprintf(stderr, " args = <%s>\n", args);
- return;
- }
-
- if(type == 't') {
- dump_t_f_port(o, name, doc);
- return;
- }
-
- o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
- o << " <desc>Set Value of " << doc << "</desc>\n";
- if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
- o << " <param_" << type << " symbol=\"x\"";
- units(o, meta["unit"]);
- o << ">\n";
- o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
- o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
- o << " </param_" << type << ">";
- } else if(meta.find("enumerated") != meta.end()) {
- o << " <param_" << type << " symbol=\"x\">\n";
- o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
- o << enum_max(meta) << "\">\n";
- add_options(o, meta);
- o << " </range_min_max>\n";
- o << " </param_" << type << ">\n";
- } else {
- o << " <param_" << type << " symbol=\"x\"";
- units(o, meta["unit"]);
- o << "/>\n";
- }
- o << " </message_in>\n";
- o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
- o << " <desc>Get Value of " << doc << "</desc>\n";
- o << " </message_in>\n";
- o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
- o << " <desc>Value of " << doc << "</desc>\n";
- if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
- o << " <param_" << type << " symbol=\"x\"";
- units(o, meta["unit"]);
- o << ">\n";
- o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
- o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
- o << " </param_" << type << ">\n";
- } else if(meta.find("enumerated") != meta.end()) {
- o << " <param_" << type << " symbol=\"x\">\n";
- o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
- o << enum_max(meta) << "\">\n";
- add_options(o, meta);
- o << " </range_min_max>\n";
- o << " </param_" << type << ">\n";
- } else {
- o << " <param_" << type << " symbol=\"x\"";
- units(o, meta["unit"]);
- o << "/>\n";
- }
- o << " </message_out>\n";
- } else if(mdoc != meta.end() && (!args || args == std::string(""))) {
- dump_any_port(o, name, doc);
- } else if(mdoc != meta.end() && args) {
- dump_generic_port(o, name, doc, args);
- } else if(mdoc != meta.end()) {
- fprintf(stderr, "Skipping \"%s\"\n", name);
- if(args) {
- fprintf(stderr, " type = %s\n", args);
- }
- } else
- fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name);
- }
-
- std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter)
- {
- o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- o << "<osc_unit format_version=\"1.0\">\n";
- o << " <meta>\n";
- o << " <name>" << formatter.prog_name << "</name>\n";
- o << " <uri>" << formatter.uri << "</uri>\n";
- o << " <doc_origin>" << formatter.doc_origin << "</doc_origin>\n";
- o << " <author><firstname>" << formatter.author_first;
- o << "</firstname><lastname>" << formatter.author_last << "</lastname></author>\n";
- o << " </meta>\n";
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- walk_ports2(formatter.p, buffer, 1024, &o, dump_ports_cb);
- o << "</osc_unit>\n";
- return o;
- }
|