O2 2.0
A communication protocol for interactive music and media applications.
o2node.h
1/* o2node.h -- hash nodes and service providers base class */
2
3/* Roger B. Dannenberg
4 * Dec 2020
5 */
6
7#ifndef O2NODE_H
8#define O2NODE_H
9
10/* Most O2 objects are tagged so we can inspect types at runtime.
11An alternative would be to have a virtual "what am I?" function,
12or even a bunch of virtual functions so that everytime computation
13depends on the type, you call a special virtual function to do it.
14Although it's more "pure OOP" that way, it gets really hard to
15read the code when special cases are scattered across multiple
16subclasses. We do a little of both in O2 implementation. Here
17are all the possible tag values:
18*/
19
20// these tags are mutually exclusive, so if you want to
21// figure out one thing to call a node, these bits sort it out:
22#define O2TAG_EMPTY 1 // just an O2node that redirects to full path table
23#define O2TAG_HASH 2
24#define O2TAG_HANDLER 4
25#define O2TAG_SERVICES 8
26#define O2TAG_PROC_TCP_SERVER 0x10
27#define O2TAG_PROC_NOMSGYET 0x20
28#define O2TAG_PROC 0x40
29#define O2TAG_PROC_TEMP 0x80
30#define O2TAG_MQTT 0x100
31#define O2TAG_OSC_UDP_SERVER 0x200
32#define O2TAG_OSC_TCP_SERVER 0x400
33#define O2TAG_OSC_UDP_CLIENT 0x800
34#define O2TAG_OSC_TCP_CLIENT 0x1000
35#define O2TAG_OSC_TCP_CONNECTION 0x2000
36#define O2TAG_HTTP_SERVER 0x4000
37#define O2TAG_HTTP_READER 0x8000
38
39// all O2TAG_BRIDGE means a Bridge_info, subclassed by
40// O2lite_info, O2sm_info, Htpp_conn (handling webscockets)
41// These are distinguished by Bridge_info->proto == one of
42// o2lite_protocol, o2sm_protocol, o2ws_protocol
43#define O2TAG_BRIDGE 0x10000
44
45#define O2TAG_STUN 0x20000
46#define O2TAG_ZC 0x40000 // zeroconf interface (Zc_info)
47#define O2TAG_TYPE_BITS ((O2TAG_ZC << 1) - 1) // mask to get just the type
48
49// The following (2) bits are used for properties. We could have implemented
50// virtual methods to get each property or even stored the SYNCED property
51// as a boolean, but this seems easier.
52#define O2TAG_SYNCED 0x80000 // sync state of PROC, MQTT, BRIDGE, websocket
53// Some objects are owned by the path_tree and must be deleted when removed
54// from the tree. Other objects can be shared by multiple entries in the
55// path_tree and are owned by an Fds_info object. Set the initial tag with or
56// without this bit accordingly:
57#define O2TAG_OWNED_BY_TREE 0x100000
58
59#define O2TAG_HIGH O2TAG_OWNED_BY_TREE
60
61
62#define ISA_HANDLER(x) ((x)->tag & O2TAG_HANDLER)
63#define ISA_HASH(x) ((x)->tag & O2TAG_HASH)
64#define ISA_SERVICES(x) ((x)->tag & O2TAG_SERVICES)
65#define ISA_PROC(x) ((x)->tag & O2TAG_PROC)
66#define ISA_PROC_TCP_SERVER(x) ((x)->tag & O2TAG_PROC_TCP_SERVER)
67#define ISA_MQTT(x) ((x)->tag & O2TAG_MQTT)
68#define ISA_OSC_UDP_CLIENT(x) ((x)->tag & O2TAG_OSC_UDP_CLIENT)
69#define ISA_OSC_TCP_CLIENT(x) ((x)->tag & O2TAG_OSC_TCP_CLIENT)
70#define ISA_BRIDGE(x) ((x)->tag & O2TAG_BRIDGE)
71#define ISA_HTTP_SERVER(x) ((x)->tag & O2TAG_HTTP_SERVER)
72#define ISA_STUN_CONN(x) ((x)->tag == O2TAG_STUN)
73
74
75// ISA_PROXY tells us if the node, considered as a service provider,
76// acts as a reference to (proxy for) another thread or
77// process. I.e. do we forward the message to another location? OSC
78// and BRIDE nodes are considered proxies.
79#define ISA_PROXY(x) \
80 ((x)->tag & (O2TAG_PROC | O2TAG_MQTT | O2TAG_OSC_UDP_CLIENT | \
81 O2TAG_OSC_TCP_CLIENT | O2TAG_BRIDGE))
82
83// ISA_REMOTE_PROC tells us if the node represents an O2 process that
84// is not the local process, e.g. the name is @pip:iip:port and the
85// node is either a connected proc or a process known through the MQTT
86// protocol:
87#define ISA_REMOTE_PROC(x) ((x)->tag & (O2TAG_PROC | O2TAG_MQTT))
88
89// HANDLER_IS_LOCAL tells us if the node represents a service handled
90// directly by the local process, e.g. it is a handler (callback
91// function), a hash table (a tree of path nodes with handlers at the
92// leaves), or empty (which directs the message sender to a direct
93// path lookup in o2_ctx->full_path_table:
94#define HANDLER_IS_LOCAL(x) \
95 ((x)->tag & (O2TAG_EMPTY | O2TAG_HASH | O2TAG_HANDLER))
96
97// ISA_LOCAL_SERVICE tells us if the service is associated with the
98// local process. This includes connections via osc, o2lite, shared
99// memory and other bridge protocols because to remote processes, the
100// services offered by all these service providers appear as services
101// of the local process:
102#define ISA_LOCAL_SERVICE(x) \
103 ((x)->tag & (O2TAG_EMPTY | O2TAG_HASH | O2TAG_HANDLER | \
104 O2TAG_OSC_UDP_CLIENT | O2TAG_OSC_TCP_CLIENT | O2TAG_BRIDGE))
105
106
107// Properties:
108#define IS_SYNCED(x) ((x)->tag & O2TAG_SYNCED)
109
110
111/* compute the size of a string including EOS and padding to next word */
112int o2_strsize(const char *s);
113
114void o2_string_pad(char *dst, const char *src);
115
116O2string o2_heapify(const char *path);
117
122// The structure of an entry in hash table. o2_node
123// subclasses are:
124// hash_node,
125// handler_entry,
126// services_entry,
127// osc_info,
128// bridge_inst
129// Any O2node can be an entry in a hash table, so hash tables
130// can be used to form trees with named links, i.e. path trees
131// for O2 address search.
132class O2node : public O2obj {
133 public:
134 int tag;
135 O2string key; // key is "owned" by this generic entry struct
136 O2node *next;
137 O2node(const char *key_, int tag_) {
138 tag = tag_;
139 key = (key_ ? o2_heapify(key_) : NULL);
140 next = NULL;
141 }
142 virtual ~O2node() { if (key) O2_FREE((char *) key); }
143
144 // Get the process that offers this service. If not remote, it's
145 // just _o2, so that's the default. Proc_info overrides this method:
146 // if proc has a key, return it; if it is o2_ctx->proc, return "_o2"
147 virtual const char *get_proc_name() { return "_o2"; }
148
149 virtual O2status status(const char **process) {
150 assert(HANDLER_IS_LOCAL(this));
151 if (process) {
152 *process = get_proc_name();
153 }
155 }
156
157#ifndef O2_NO_DEBUG
158 virtual void show(int indent);
159#endif
160};
161
162
163// Hash table node, another hash table
164class Hash_node : public O2node { // "subclass" of o2_node
165 friend class Enumerate;
166 int num_children;
167 Vec<O2node *> children; // children is a dynamic array of o2_node_ptr.
168 public:
169 // The key (name) of this entry. key is owned by the caller and
170 // a copy is made and owned by the node.
171 Hash_node(const char *key_) :
172 O2node(key_, O2TAG_HASH | O2TAG_OWNED_BY_TREE) {
173 num_children = 0;
174 table_init(2);
175 }
176 // avoid any memory allocation if no parameters:
177 Hash_node() : O2node(NULL, O2TAG_HASH) {
178 num_children = 0;
179 table_init(0);
180 }
181 void finish();
182 virtual ~Hash_node() { finish(); }
183
184 bool empty() { return num_children == 0; }
185#ifndef O2_NO_DEBUG
186 void show(int indent);
187#endif
188 O2node **lookup(O2string key);
189
190 // At the top level, all children are services_entry_ptrs (tag
191 // O2TAG_SERVICES). Below that level children can have tags O2TAG_HASH
192 // or O2TAG_HANDLER.
193 O2err entry_insert_at(O2node **loc, O2node *entry);
195 O2err insert(O2node *entry);
196 Hash_node *tree_insert_node(O2string key);
197 O2err entry_remove_by_name(O2string key);
198 O2err entry_remove(O2node **child, bool resize);
199 protected:
200 void table_init(int locations) {
201 children.init(locations, true);
202 children.append_space(children.get_allocated());
203 }
204 O2err table_resize(int new_locs);
205};
206
207#ifdef O2_NO_DEBUG
208#define TO_HASH_NODE(node) ((Hash_node *) node)
209#else
210#define TO_HASH_NODE(node) (assert(ISA_HASH(node)), \
211 (Hash_node *) (node))
212#endif
213
214// enumerate is used to visit all entries in a hash table
215// it is used for:
216// - enumerating services with status change when we become the
217// reference clock process
218// - enumerating all services offered by a given process when
219// that process's clock status changes
220// - enumerating local services to send to another process
221// - enumerating node entries in o2_node_show
222// - enumerating node entries for pattern matching an address
223// - enumerating all services to look for tappers that
224// match a deleted process
225// - enumerating all services to look for services offered by
226// a deleted process
227// - enumerating all entries to rehash them into a different
228// size of table
229// - enumerating services that belong to process to show service
230// names in o2_sockets_show()
231//
232// To enumerate elements of a hash table (at one level), use this
233// structure and see o2_enumerate_begin(), o2_enumerate_next()
235 Vec<O2node *> *dict;
236 int index;
237 O2node *entry;
238 public:
239 Enumerate(Hash_node *hn) { dict = &hn->children; index = 0; entry = NULL; }
240 Enumerate(Vec<O2node *> *vec) { dict = vec; index = 0; entry = NULL; }
241 O2node *next();
242};
243
244
245// Hash table's entry for handler
246class Handler_entry : public O2node { // "subclass" of o2_node
247public:
248 O2method_handler handler;
249 const void *user_data;
250 O2string full_path; // this is the key for this entry in the
251 // o2_ctx->full_path_table; it is a copy of the key in the
252
253 // path_tree table entry, so you should never free this pointer
254 // -- it will be freed when the path_tree table entry is freed.
255 // (Exception: if O2_NO_PATTERNS, there is no path_tree.)
256 O2string type_string;
263 public:
264 Handler_entry(const char *key, O2method_handler h, const void *user_data_,
265 O2string full_path_, O2string type_string_, int types_len_,
266 bool coerce_flag_, bool parse_args_) :
267 O2node(key, O2TAG_HANDLER | O2TAG_OWNED_BY_TREE) {
268 handler = h; user_data = user_data_; full_path = full_path_;
269 type_string = type_string_; types_len = types_len_;
270 coerce_flag = coerce_flag_; parse_args = parse_args_;
271 }
272 // copies everything except full_path, which is set to NULL, also
273 // makes a full copy of type_string if any.
274 Handler_entry(Handler_entry *src) : O2node(src->full_path, O2TAG_HANDLER) {
275 handler = src->handler; user_data = src->user_data;
276 full_path = NULL; type_string = src->type_string;
277 if (type_string) type_string = o2_heapify(type_string);
279 parse_args = src->parse_args;
280 }
281 virtual ~Handler_entry();
282 void invoke(o2_msg_data_ptr msg, const char *types);
283#ifndef O2_NO_DEBUG
284 void show(int indent);
285#endif
286};
287
288
289// A message handler that uses a socket or other connection to
290// deliver messages remotely
291class Proxy_info : public O2node, public Net_interface {
292public:
293 Proxy_info(const char *key, int tag) : O2node(key, tag) { fds_info = NULL; }
294 // remove is only called from ~Fds_info(): it's purpose is to delete
295 // a Proxy without a recursive deletion of Fds_info that it links to.
296 virtual void remove() { fds_info = NULL; delete this; }
297
298 void delete_fds_info() {
299 if (fds_info) {
300 fds_info->owner = NULL; // remove socket-to-node_info pointer
301 // not sure who calls this or why, so do a "polite" close where
302 // we wait for socket to become writeable before closing it
303 fds_info->close_socket(false); // shut down the connection
304 }
305 }
306
307 // Tell this proxy that the local process is synchronized with the
308 // global clock (it may even be the *source* of the global clock).
309 // Not all proxies care, e.g. Stun_info is a subclass of Proxy_info,
310 // but it does not handle O2 messages or care about clock sync.
311 // If the Proxy represents a process that has clock sync
312 // status, e.g. O2_REMOTE vs O2_REMOTE_NOTIME or O2_BRIDGE vs
313 // O2_BRIDGE_NOTIME, and the remote process is synchronized, e.g.
314 // PROC_SYNC instead of PROC_NOCLOCK, then return true
315 // so that the change can be reported to the application via /_o2/si.
316 // Note that if the clock is a 3rd party, the remote process could be
317 // synchronized before we are, so it becomes "synchronized with us"
318 // when we go from LOCAL_NOTIME to LOCAL, i.e. globally synchronized.
319 virtual bool local_is_synchronized() { return false; }
320
321 // Proxy tells whether to send messages ahead of time or to schedule
322 // them locally and send according to timestamp. Return false if the
323 // destination process has a scheduler to handle timestamped
324 // messages. Note that OSC schedules bundles but not regular messages,
325 // so the result depends on the message. The callee can assume there is
326 // a pending message that can be accessed by o2_current_message().
327 virtual bool schedule_before_send() { return false; };
328 virtual O2err send(bool block) {
329 o2_drop_message("Proxy::send called by mistake", true);
330 return O2_FAIL; }
331 virtual O2err deliver(O2netmsg_ptr msg);
332 // returns message to send, or null if O2_NO_SERVICE:
333 O2message_ptr pre_send(int *tcp_flag);
334#ifndef O2_NO_DEBUG
335 // to print debugging information on connections (O2_DBc):
336 void co_info(Fds_info *fds_info, const char *msg) {
337 if (!fds_info) return;
338 printf("%s %s (%s)\n socket %ld index %d tags %s, %s\n",
339 o2_debug_prefix, msg, key ? key : "noname",
340 (long) fds_info->get_socket(), fds_info->fds_index,
341 o2_tag_to_string(fds_info->net_tag), o2_tag_to_string(tag));
342 }
343
344#endif
345};
346
347extern Proxy_info *o2_message_source;
348
349
350#ifdef O2_NO_DEBUG
351#define TO_HANDLER_ENTRY(node) ((Handler_entry *) node)
352#else
353#define TO_HANDLER_ENTRY(node) (assert(ISA_HANDLER(node)), \
354 (Handler_entry *) node)
355#endif
356
357#ifndef O2_NO_DEBUG
358void o2_fds_info_debug_predelete(Fds_info *info);
359#endif
360
361#endif /* HASHNODE_H */
362
Definition: o2node.h:234
Definition: o2network.h:147
Definition: o2node.h:246
int coerce_flag
Definition: o2node.h:258
int types_len
the length of type_string
Definition: o2node.h:257
O2string type_string
types expected by handler, or NULL to ignore
Definition: o2node.h:256
int parse_args
boolean - send argc and argv to handler?
Definition: o2node.h:262
Definition: o2node.h:164
O2err insert(O2node *entry)
add an entry to a hash table
Definition: hashnode.cpp:45
Definition: o2network.h:121
Definition: o2node.h:132
Definition: o2obj.h:9
Definition: o2node.h:291
Definition: vec.h:6
void o2_drop_message(const char *warn, bool free_the_msg)
Tell world that a message was dropped.
Definition: msgsend.cpp:209
bool o2_clock_is_synchronized
A variable indicating that the clock is the reference or is synchronized to the reference.
Definition: clock.cpp:26
void(* O2method_handler)(const o2_msg_data_ptr msg, const char *types, O2arg_ptr *argv, int argc, const void *user_data)
callback function to receive an O2 message
Definition: o2.h:880
O2status
Status return codes for the o2_status function.
Definition: o2.h:435
O2err
return values used generally by O2 functions
Definition: o2.h:329
@ O2_LOCAL
local service with clock sync.
Definition: o2.h:496
@ O2_LOCAL_NOTIME
local service, no clock sync yet.
Definition: o2.h:445
@ O2_FAIL
a non-specific error occurred.
Definition: o2.h:339
an O2 message container
Definition: o2.h:690
data part of an O2 message
Definition: o2.h:638
Definition: o2network.h:57