Real Time Open Sound Control librtosc
 All Classes Namespaces Files Functions Variables Typedefs Macros Pages
ports.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Mark McCurry
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #ifndef RTOSC_PORTS
26 #define RTOSC_PORTS
27 
28 #include <vector>
29 #include <functional>
30 #include <initializer_list>
31 #include <rtosc/rtosc.h>
32 #include <rtosc/matcher.h>
33 #include <cstring>
34 #include <cctype>
35 #include <cstdlib>
36 #include <cstdio>
37 
38 namespace rtosc {
39 
40 //First define all types
41 typedef const char *msg_t;
42 
43 struct Port;
44 struct Ports;
45 
46 struct RtData
47 {
48  char *loc;
49  size_t loc_size;
50  void *obj;
51 };
52 
56 struct Port {
57  const char *name; //< Pattern for messages to match
58  const char *metadata;//< Statically accessable data about port
59  Ports *ports; //< Pointer to further ports
60  std::function<void(msg_t, RtData)> cb;//< Callback for matching functions
61 };
62 
63 static void scat(char *dest, const char *src);
64 
75 struct Ports
76 {
77  std::vector<Port> ports;
78 
80  auto begin() const -> decltype(ports.begin())
81  {
82  return ports.begin();
83  }
84 
86  auto end() const -> decltype(ports.end())
87  {
88  return ports.end();
89  }
90 
92  const Port &operator[](unsigned i) const
93  {
94  return ports[i];
95  }
96 
97  Ports(std::initializer_list<Port> l)
98  :ports(l)
99  {}
100 
101  Ports(const Ports&) = delete;
102 
112  void dispatch(char *loc, size_t loc_size, msg_t m, void *v)
113  {
114  //simple case [very very cheap]
115  if(!loc || !loc_size) {
116  for(Port &port: ports) {
117  if(rtosc_match(port.name,m))
118  port.cb(m,{NULL,0,v});
119  }
120  } else { //somewhat cheap
121 
122  //TODO this function is certainly buggy at the moment, some tests
123  //are needed to make it clean
124  //XXX buffer_size is not properly handled yet
125  if(loc[0] == 0)
126  loc[0] = '/';
127 
128  char *old_end = loc;
129  while(*old_end) ++old_end;
130 
131  for(const Port &port: ports) {
132  if(!rtosc_match(port.name, m))
133  continue;
134 
135  //Append the path
136  if(index(port.name,'#')) {
137  const char *msg = m;
138  char *pos = old_end;
139  while(*msg && *msg != '/')
140  *pos++ = *msg++;
141  *pos = '/';
142  } else
143  scat(loc, port.name);
144 
145  //Apply callback
146  port.cb(m,{loc,loc_size,v});
147 
148  //Remove the rest of the path
149  char *tmp = old_end;
150  while(*tmp) *tmp++=0;
151  }
152  }
153  }
154 
159  const Port *operator[](const char *name) const
160  {
161  for(const Port &port:ports) {
162  const char *_needle = name,
163  *_haystack = port.name;
164  while(*_needle && *_needle==*_haystack)_needle++,_haystack++;
165 
166  if(*_needle == 0 && *_haystack == ':') {
167  return &port;
168  }
169  }
170  return NULL;
171  }
172 
173  //util
174  msg_t snip(msg_t m) const
175  {
176  while(*m && *m != '/') ++m;
177  return m+1;
178  }
179 
181  const Port *apropos(const char *path) const
182  {
183  if(path && path[0] == '/')
184  ++path;
185 
186  for(const Port &port: ports)
187  if(index(port.name,'/') && rtosc_match_path(port.name,path))
188  return (index(path,'/')[1]==0) ? &port :
189  port.ports->apropos(this->snip(path));
190 
191  //This is the lowest level, now find the best port
192  for(const Port &port: ports)
193  if(*path && strstr(port.name, path)==port.name)
194  return &port;
195 
196  return NULL;
197  }
198 };
199 
200 
201 /*********************
202  * Port walking code *
203  *********************/
204 static void scat(char *dest, const char *src)
205 {
206  while(*dest) dest++;
207  if(*dest) dest++;
208  while(*src && *src!=':') *dest++ = *src++;
209  *dest = 0;
210 }
211 
212 typedef std::function<void(const Port*,const char*)> port_walker_t;
213 
214 static
215 void walk_ports(const Ports *base,
216  char *name_buffer,
217  size_t buffer_size,
218  port_walker_t walker)
219 {
220  //XXX buffer_size is not properly handled yet
221  if(name_buffer[0] == 0)
222  name_buffer[0] = '/';
223 
224  char *old_end = name_buffer;
225  while(*old_end) ++old_end;
226 
227  for(const Port &p: *base) {
228  if(index(p.name, '/')) {//it is another tree
229  if(index(p.name,'#')) {
230  const char *name = p.name;
231  char *pos = old_end;
232  while(*name != '#') *pos++ = *name++;
233  const unsigned max = atoi(name+1);
234 
235  for(unsigned i=0; i<max; ++i)
236  {
237  sprintf(pos,"%d",i);
238 
239  //Ensure the result is a path
240  if(rindex(name_buffer, '/')[1] != '/')
241  strcat(name_buffer, "/");
242 
243  //Recurse
244  walk_ports(p.ports, name_buffer, buffer_size, walker);
245  }
246  } else {
247  //Append the path
248  scat(name_buffer, p.name);
249 
250  //Recurse
251  walk_ports(p.ports, name_buffer, buffer_size, walker);
252  }
253  } else {
254  //Append the path
255  scat(name_buffer, p.name);
256 
257  //Apply walker function
258  walker(&p, name_buffer);
259  }
260 
261  //Remove the rest of the path
262  char *tmp = old_end;
263  while(*tmp) *tmp++=0;
264  }
265 }
266 };
267 
268 
269 #endif