1 /*  =========================================================================
2     ztrie - simple trie for tokenizable strings
3 
4     Copyright (c) the Contributors as noted in the AUTHORS file.
5     This file is part of CZMQ, the high-level C binding for 0MQ:
6     http://czmq.zeromq.org.
7 
8     This Source Code Form is subject to the terms of the Mozilla Public
9     License, v. 2.0. If a copy of the MPL was not distributed with this
10     file, You can obtain one at http://mozilla.org/MPL/2.0/.
11     =========================================================================
12 */
13 
14 /*
15 @header
16     This is a variant of a trie or prefix tree where all the descendants of a
17     node have a common prefix of the string associated with that node. This
18     implementation is specialized for strings that can be tokenized by a delimiter
19     like a URL, URI or URN. Routes in the tree can be matched by regular expressions
20     and by using capturing groups parts of a matched route can be easily obtained.
21 @discuss
22     Note that the performance for pure string based matching is okay but on short
23     strings zhash and zhashx are 3-4 times faster.
24 @end
25 */
26 
27 #include "czmq_classes.h"
28 
29 #define MODE_INSERT 0
30 #define MODE_LOOKUP 1
31 #define MODE_MATCH  2
32 
33 #define NODE_TYPE_STRING    0  //  Node with string token
34 #define NODE_TYPE_REGEX     1  //  Node with regex token
35 #define NODE_TYPE_PARAM     2  //  Node with named regex token and capturing group(s)
36 #define NODE_TYPE_ASTERISK  3  //  Node with an asterisk, to match routes of variable length
37 
38 #define MIN_LEN(x,y) \
39     y + ((x - y) & ((x - y) >>(sizeof(int) * CHAR_BIT - 1)))
40 
41 
42 // TODO: Move to a more appropriate location:
43 static char *
s_strndup(const char * s,size_t size)44 s_strndup (const char *s, size_t size) {
45     char *dup;
46     char *end = (char *) memchr (s, '\0', size);
47     if (end)
48         size = end - 1 - s;     //  -1 to get the last char before '\0'
49     dup = (char *) zmalloc (sizeof (char) * size + 1); //  +1 for trailing '\0'
50     if (size) {
51         memcpy (dup, s, size);
52         dup [size] = '\0';
53     }
54     return dup;
55 }
56 
57 //  Trie node, used internally only
58 
59 typedef struct _ztrie_node_t {
60     char *token;         //  Part of a path between to delemiters e.g. '/{token}/'
61     int token_type;      //  Type of the token, string, regex, asterisk
62     int token_len;       //  Number of characters of the token
63     size_t path_len;     //  Length of the path/route including this token
64     bool endpoint;       //  Has a path been added that routes to this node?
65     size_t parameter_count;  //  How many regex parameters does this token contain?
66     char **parameter_names;  //  Names of the regex parameters for easy access at matching time
67     char **parameter_values; //  Values of the parameters
68     char *asterisk_match;    //  Matched asterisk route
69     zrex_t *regex;           //  Compiled regex
70     void *data;              //  Custom arbitrary data assoziated with a route
71     ztrie_destroy_data_fn *destroy_data_fn;  // Callback to destroy custom data
72     zlistx_t *children;       //  List of all children to this route
73     struct _ztrie_node_t *parent;  // Parent of this node
74 } ztrie_node_t;
75 
76 //  --------------------------------------------------------------------------
77 //  Structure of our class
78 
79 struct _ztrie_t {
80     char delimiter;         //  Character that seperates the tokens of a route
81     ztrie_node_t *root;     //  Root node of this tree
82     ztrie_node_t *match;    //  Last match made by ztrie_matches
83     zlistx_t *params;       //  List of regex parameters found during parsing
84                             //  The list is kept globally to optimize performance.
85 };
86 
87 //  Internal helper functions
88 
89 static int
s_ztrie_node_compare(const void * item1,const void * item2)90 s_ztrie_node_compare (const void *item1, const void *item2) {
91     assert (item1);
92     assert (item2);
93     ztrie_node_t *node1 = (ztrie_node_t *) item1;
94     ztrie_node_t *node2 = (ztrie_node_t *) item2;
95 
96     int same = node1->token_type - node2->token_type;
97     if (same == 0) {
98         if (*node1->token == *node2->token  //  This achieves a small performance boost
99             && node1->token_len == node2->token_len
100             && strncmp (node1->token, node2->token, MIN_LEN (node1->token_len, node2->token_len)) == 0)
101             return 0;
102         else
103             return -1;
104     }
105     else
106          return same;
107 }
108 
109 
110 //  Create a new ztrie_node. If parent is not NULL the child will add itself
111 //  to the parents children.
112 
113 static ztrie_node_t *
s_ztrie_node_new(ztrie_node_t * parent,char * token,int token_len,zlistx_t * param_keys,int token_type)114 s_ztrie_node_new (ztrie_node_t *parent, char *token, int token_len, zlistx_t *param_keys, int token_type)
115 {
116     ztrie_node_t *self = (ztrie_node_t *) zmalloc (sizeof (ztrie_node_t));
117     assert (self);
118 
119     //  Initialize properties
120     self->token = s_strndup (token, token_len);
121     self->token_type = token_type;
122     self->token_len = token_len;
123     self->endpoint = false;
124     self->parameter_count = 0;
125     self->parameter_names = NULL;
126     self->parameter_values = NULL;
127     if (param_keys && zlistx_size (param_keys) > 0) {
128         self->parameter_count = zlistx_size (param_keys);
129         self->parameter_names = (char **) malloc (sizeof (char *) * self->parameter_count);
130         self->parameter_values = (char **) malloc (sizeof (char *) * self->parameter_count);
131         char *key = (char *) zlistx_first (param_keys);
132         size_t index;
133         for (index = 0; index < zlistx_size (param_keys); index++) {
134             self->parameter_names [index] = key;
135             self->parameter_values [index] = NULL;
136             key = (char *) zlistx_next (param_keys);
137         }
138     }
139     if (self->token_type == NODE_TYPE_REGEX || self->token_type == NODE_TYPE_PARAM)
140         self->regex = zrex_new (self->token);
141     self->data = NULL;
142     //  Initialize relationships
143     self->parent = parent;
144     if (self->parent) {
145         zlistx_add_end (self->parent->children, self);
146         //  Sort regexes to the end to avoid conlficts
147         zlistx_sort (self->parent->children);
148     }
149     size_t parent_path_len = self->parent? self->parent->path_len: 0;
150     self->path_len = parent_path_len + strlen (self->token) + 1; // +1 path delimiter
151     self->children = zlistx_new ();
152     zlistx_set_comparator (self->children, s_ztrie_node_compare);
153 
154     return self;
155 }
156 
157 
158 //  Destroy the ztrie_node.
159 
160 static void
s_ztrie_node_destroy(ztrie_node_t ** self_p)161 s_ztrie_node_destroy (ztrie_node_t **self_p)
162 {
163     assert (self_p);
164     if (*self_p) {
165         ztrie_node_t *self = *self_p;
166 
167         //  Free class properties
168         zstr_free (&self->token);
169         zstr_free (&self->asterisk_match);
170         if (self->parameter_count > 0) {
171             size_t index;
172             for (index = 0; index < self->parameter_count; index++) {
173                 freen (self->parameter_names [index]);
174                 if (self->parameter_values [index])
175                     freen (self->parameter_values [index]);
176             }
177             freen (self->parameter_names);
178             freen (self->parameter_values);
179         }
180         if (self->token_type == NODE_TYPE_REGEX || self->token_type == NODE_TYPE_PARAM)
181             zrex_destroy (&self->regex);
182         zlistx_destroy (&self->children);
183         if (self->data && self->destroy_data_fn)
184             (self->destroy_data_fn) (&self->data);
185 
186         //  Free object itself
187         freen (self);
188         *self_p = NULL;
189     }
190 }
191 
192 
193 //  Update the value of a regex parameter at position.
194 
195 static void
s_ztrie_node_update_param(ztrie_node_t * self,int pos,const char * value)196 s_ztrie_node_update_param (ztrie_node_t *self, int pos, const char *value)
197 {
198     assert (self);
199     zstr_free (&self->parameter_values [pos - 1]);
200     self->parameter_values [pos - 1] = strdup (value);
201 }
202 
203 
204 //  --------------------------------------------------------------------------
205 //  Creates a new ztrie.
206 
207 ztrie_t *
ztrie_new(char delimiter)208 ztrie_new (char delimiter)
209 {
210     ztrie_t *self = (ztrie_t *) zmalloc (sizeof (ztrie_t));
211     assert (self);
212 
213     //  Initialize properties
214     if (delimiter)
215         self->delimiter = delimiter;
216     else
217         self->delimiter = '/';
218     self->root = s_ztrie_node_new (NULL, "", 0,  NULL, NODE_TYPE_STRING);
219     self->match = NULL;
220     self->params = zlistx_new ();
221 
222     return self;
223 }
224 
225 //  Destroy all children of a node
226 
227 static void
s_ztrie_destroy_children(ztrie_node_t * node)228 s_ztrie_destroy_children (ztrie_node_t *node)
229 {
230     ztrie_node_t *child = (ztrie_node_t *) zlistx_first (node->children);
231     while (child) {
232         s_ztrie_destroy_children (child);
233         s_ztrie_node_destroy (&child);
234         child = (ztrie_node_t *) zlistx_next (node->children);
235     }
236 }
237 
238 //  --------------------------------------------------------------------------
239 //  Destroy the ztrie.
240 
241 void
ztrie_destroy(ztrie_t ** self_p)242 ztrie_destroy (ztrie_t **self_p)
243 {
244     assert (self_p);
245     if (*self_p) {
246         ztrie_t *self = *self_p;
247 
248         //  Free class properties
249         s_ztrie_destroy_children (self->root);
250         s_ztrie_node_destroy (&self->root);
251         zlistx_destroy (&self->params);
252 
253         //  Free object itself
254         freen (self);
255         *self_p = NULL;
256     }
257 }
258 
259 //  String compare token
260 
261 static ztrie_node_t *
s_ztrie_compare_token(ztrie_node_t * parent,char * token,int len)262 s_ztrie_compare_token (ztrie_node_t *parent, char *token, int len)
263 {
264     ztrie_node_t *child = (ztrie_node_t *) zlistx_first (parent->children);
265     while (child) {
266         if (child->token_len == len &&
267             strncmp (child->token, token, MIN_LEN (child->token_len, len)) == 0)
268             return child;
269         child = (ztrie_node_t *) zlistx_next (parent->children);
270     }
271     return NULL;
272 }
273 
274 //  String compare token or evaluate regexes
275 
276 static ztrie_node_t *
s_ztrie_matches_token(ztrie_node_t * parent,char * token,int len)277 s_ztrie_matches_token (ztrie_node_t *parent, char *token, int len)
278 {
279     char firstbyte = *token;
280     ztrie_node_t *child = (ztrie_node_t *) zlistx_first (parent->children);
281     while (child) {
282         if (child->token_type == NODE_TYPE_STRING) {
283             if (firstbyte == *child->token  //  This achieves a small performance boost
284                 && child->token_len == len
285                 && strncmp (child->token, token, MIN_LEN (child->token_len, len)) == 0)
286                     return child;
287         }
288         else
289         if (child->token_type == NODE_TYPE_ASTERISK) {
290             child->asterisk_match = strdup (token);
291             return child;
292         }
293         else {
294             //  Need to copy token as zrex_matches expects '\0' terminated string
295             char *token_term = s_strndup (token, len);
296             if (zrex_matches (child->regex, token_term)) {
297                 if (child->token_type == NODE_TYPE_PARAM) {
298                     //  One hit means no capturing group was defined
299                     //  More than one hit indicates that at least on capturing group.
300                     //  In this case only the values of the capturing groups are considered.
301                     if (zrex_hits (child->regex) == 1)
302                         s_ztrie_node_update_param (child, 1, zrex_hit (child->regex, 0));
303                     else
304                     if (zrex_hits (child->regex) > 1) {
305                         int index;
306                         for (index = 1; index < zrex_hits (child->regex); index++)
307                             s_ztrie_node_update_param (child, index, zrex_hit (child->regex, index));
308                     }
309                 }
310                 freen (token_term);
311                 return child;
312             }
313             freen (token_term);
314         }
315         child = (ztrie_node_t *) zlistx_next (parent->children);
316     }
317     return NULL;
318 }
319 
320 
321 //  --------------------------------------------------------------------------
322 //  Parses a path bytewise trying to find matches for path tokens. Depending
323 //  on the mode there are different behaviors on,
324 //  how the tokens are compared:
325 //    MODE_INSERT:  All tokens are compared as strings.
326 //    MODE_LOOKUP:  All tokens are compared as strings.
327 //    MODE_MATCH:   Node tokens of NODE_TYPE_STRING tokens are compared as strings,
328 //                  otherwise the tokens are matched against the nodes regex.
329 //  how a mismatch is handled:
330 //    MODE_INSERT: creates a new node and attaches it to the common prefix for
331 //                 the given path, repeat for the remaining path tokens.
332 //                 returns the node that has been attached last.
333 //    MODE_LOOKUP: returns NULL if the comparison failed.
334 //    MODE_MATCH:  returns NULL if the comparison failed.
335 
336 static ztrie_node_t *
s_ztrie_parse_path(ztrie_t * self,const char * path,int mode)337 s_ztrie_parse_path (ztrie_t *self, const char *path, int mode)
338 {
339     int state = 0;
340     char *needle, *beginToken = NULL, *beginRegex = NULL;
341     ztrie_node_t *parent = self->root;
342     if (zlistx_size (self->params) > 0)
343         zlistx_purge (self->params);
344 
345     size_t len = strlen (path);
346     needle = (char *) path;
347     char *needle_stop = needle + len;
348     //  Ignore trailing delimiter
349     if (needle [len-1] == self->delimiter)
350         needle_stop -= 1;
351     while (needle < needle_stop + 1) {
352         //  It is valid not to have an delimiter at the end of the path
353         if (*needle == self->delimiter || needle == needle_stop) {
354             //  Token starts with delimiter ignore everything that comes before
355             if (state == 0) {
356                 beginToken = needle + 1;
357                 state++;
358                 if (mode == MODE_INSERT || mode == MODE_LOOKUP)
359                     // Increment so regexes are parsed which is only relevant
360                     // during INSERT or LOOKUP. Using different states gives a small
361                     // performance boost for matching.
362                     state++;
363             }
364             //  Token ends with delimiter.
365             else
366             if (state < 3) {
367                 int matchType = zlistx_size (self->params) > 0? NODE_TYPE_PARAM:
368                                     beginRegex? NODE_TYPE_REGEX: NODE_TYPE_STRING;
369                 char *matchToken = beginRegex? beginRegex: beginToken;
370                 int matchTokenLen = (int) (needle - matchToken) - (beginRegex? 1: 0);
371                 //  Illegal token
372                 if (matchTokenLen == 0)
373                     return NULL;
374                 ztrie_node_t *match = NULL;
375                 //  Asterisk nodes are only allowed at the end of a route
376                 if (needle == needle_stop && *matchToken == '*') {
377                     if (zlistx_size (parent->children) == 0) {
378                         matchType = NODE_TYPE_ASTERISK;
379                         matchToken = needle - 1;
380                         matchTokenLen = 1;
381                     }
382                     //  Asterisk must be a leaf in the tree
383                     else
384                         return NULL;
385                 }
386                 else {
387                     matchType = zlistx_size (self->params) > 0? NODE_TYPE_PARAM:
388                                         beginRegex? NODE_TYPE_REGEX: NODE_TYPE_STRING;
389                     matchToken = beginRegex? beginRegex: beginToken;
390                     matchTokenLen = (int) (needle - matchToken) - (beginRegex? 1: 0);
391                 }
392 
393                 //  In insert and lookup mode only do a string comparison
394                 if (mode == MODE_INSERT || mode == MODE_LOOKUP)
395                     match = s_ztrie_compare_token (parent, matchToken, matchTokenLen);
396                 else
397                 //  Otherwise evaluate regexes
398                 if (mode == MODE_MATCH)
399                     match = s_ztrie_matches_token (parent, matchToken, matchTokenLen);
400 
401                 //  Mismatch behavior depends on mode
402                 if (!match) {
403                     //  Append to common prefix
404                     if (mode == MODE_INSERT) {
405                         //  It's not allowed to append on asterisk
406                         if (parent->token_type == NODE_TYPE_ASTERISK ||
407                                 (zlistx_size (parent->children) == 1 &&
408                                 ((ztrie_node_t *) (zlistx_first (parent->children)))->token_type == NODE_TYPE_ASTERISK))
409                             return NULL;
410                         parent = s_ztrie_node_new (parent, matchToken, matchTokenLen, self->params, matchType);
411                     }
412                     else
413                     //  No match for path found
414                     if (mode == MODE_MATCH || mode == MODE_LOOKUP)
415                         return NULL;
416                 }
417                 //  If a match has been found it becomes the parent for next path token
418                 else {
419                     parent = match;
420                     //  In case a asterisk match has been made skip the rest of the route
421                     if (parent->token_type == NODE_TYPE_ASTERISK)
422                         break;
423                 }
424                 //  Cleanup for next token
425                 beginRegex = NULL;
426                 if (zlistx_size (self->params) > 0)
427                     zlistx_purge (self->params);
428                 //  Token end equals token begin
429                 beginToken = needle + 1;
430             }
431         }
432         else
433         //  regex starts with '{'
434         if (state == 2 && *needle == '{') {
435             beginRegex = needle + 1;
436             state++;
437         }
438         else
439         //  in the middle of the regex. Found a named regex.
440         if (state == 3 && (*needle == ':')) {
441             zlistx_add_end (self->params, s_strndup (beginRegex, needle - beginRegex));
442             beginRegex = needle + 1;
443 
444         }
445         else
446         // regex ends with {
447         if (state == 3 && *needle == '}') {
448             state--;
449         }
450         needle++;
451     }
452 
453     //  In matching mode the discovered node must be an endpoint
454     if (parent && mode == MODE_MATCH && !parent->endpoint)
455         return NULL;
456 
457     return parent;
458 }
459 
460 
461 //  --------------------------------------------------------------------------
462 //  Inserts a new route into the trie and attaches the data. Returns -1
463 //  if the route already exists, otherwise 0. This method takes ownership of
464 //  the provided data.
465 
466 int
ztrie_insert_route(ztrie_t * self,const char * path,void * data,ztrie_destroy_data_fn destroy_data_fn)467 ztrie_insert_route (ztrie_t *self, const char *path, void *data, ztrie_destroy_data_fn destroy_data_fn)
468 {
469     assert (self);
470     ztrie_node_t *node = s_ztrie_parse_path (self, path, MODE_INSERT);
471     //  If the returned node has no endpoint, a new route can be assigned to it.
472     if (node && !node->endpoint) {
473         node->endpoint = true;
474         node->data = data;
475         node->destroy_data_fn = destroy_data_fn;
476         return 0;
477     }
478     //  If the returned node has an endpoint, a route has already assigned to it.
479     else
480         return -1;
481 }
482 
483 
484 //  --------------------------------------------------------------------------
485 //  Removes a route from the trie and destroys its data. Returns -1 if the
486 //  route does not exists, otherwise 0.
487 
488 int
ztrie_remove_route(ztrie_t * self,const char * path)489 ztrie_remove_route (ztrie_t *self, const char *path)
490 {
491     assert (self);
492     ztrie_node_t *match = s_ztrie_parse_path (self, path, MODE_LOOKUP);
493     //  The path did match a node which is endpoint to a route
494     if (match && match->endpoint) {
495         //  This node is part of other routes, thus it cannot destroy it
496         if (zlistx_size (match->children) > 0) {
497             match->endpoint = false;
498             if (match->data && match->destroy_data_fn)
499                 (match->destroy_data_fn) (&match->data);
500         }
501         //  If this node is not part of other routes, destroy it
502         else {
503             //  Delete match from parent's children before destroying
504             void *handle = zlistx_find (match->parent->children, match);
505             assert (handle);
506             zlistx_delete (match->parent->children, handle);
507             s_ztrie_node_destroy (&match);
508         }
509         return 0;
510     }
511     //  If there is no match or the match is not endpoint to a route, fail
512     else
513         return -1;
514 }
515 
516 //  --------------------------------------------------------------------------
517 //  Returns true if the path matches a route in the tree, otherwise false.
518 
519 bool
ztrie_matches(ztrie_t * self,const char * path)520 ztrie_matches (ztrie_t *self, const char *path)
521 {
522     assert (self);
523     self->match = s_ztrie_parse_path (self, path, MODE_MATCH);
524     return self->match? true: false;
525 }
526 
527 
528 //  --------------------------------------------------------------------------
529 //  Returns the data of a matched route from last ztrie_matches. If the path
530 //  did not match, returns NULL. Do not delete the data as it's owned by
531 //  ztrie.
532 
533 void *
ztrie_hit_data(ztrie_t * self)534 ztrie_hit_data (ztrie_t *self)
535 {
536     assert (self);
537     if (self->match)
538         return self->match->data;
539     return NULL;
540 }
541 
542 
543 //  --------------------------------------------------------------------------
544 //  Returns the count of parameters that a matched route has.
545 
546 size_t
ztrie_hit_parameter_count(ztrie_t * self)547 ztrie_hit_parameter_count (ztrie_t *self)
548 {
549     size_t count = 0;
550     ztrie_node_t *node = self->match;
551     while (node) {
552         count += node->parameter_count;
553         node = node->parent;
554     }
555     return count;
556 }
557 
558 //  --------------------------------------------------------------------------
559 //  Returns the parameters of a matched route with named regexes from last
560 //  ztrie_matches. If the path did not match or the route did not contain any
561 //  named regexes, returns NULL. The caller is responseable for destroy the map.
562 
563 zhashx_t *
ztrie_hit_parameters(ztrie_t * self)564 ztrie_hit_parameters (ztrie_t *self)
565 {
566     assert (self);
567     if (self->match) {
568         zhashx_t *route_parameters = zhashx_new ();
569         ztrie_node_t *node = self->match;
570         while (node) {
571             size_t index;
572             for (index = 0; index < node->parameter_count; index++)
573                 zhashx_insert (route_parameters,
574                                node->parameter_names [index],
575                                (void *) node->parameter_values [index]);
576             node = node->parent;
577         }
578         return route_parameters;
579     }
580     return NULL;
581 }
582 
583 
584 //  --------------------------------------------------------------------------
585 //  Returns the asterisk matched part of a route, if there has been no match
586 //  or no asterisk match, returns NULL.
587 
588 const char *
ztrie_hit_asterisk_match(ztrie_t * self)589 ztrie_hit_asterisk_match (ztrie_t *self)
590 {
591     assert (self);
592     if (self->match)
593         return self->match->asterisk_match;
594     return NULL;
595 }
596 
597 //  --------------------------------------------------------------------------
598 //  Print properties of the ztrie object.
599 //
600 
601 static void
s_ztrie_print_tree_line(ztrie_node_t * self,bool end_line)602 s_ztrie_print_tree_line (ztrie_node_t *self, bool end_line)
603 {
604     if (self->parent) {
605         s_ztrie_print_tree_line (self->parent, false);
606         if (zlistx_tail (self->parent->children) == self) {
607             if (end_line)
608                 printf ("`-- ");
609             else
610                 printf ("    ");
611         }
612         else {
613             if (end_line)
614                 printf ("+-- ");
615             else
616                 printf ("|   ");
617         }
618         if (end_line) {
619             char *is_endpoint = self->endpoint? "true": "false";
620             printf ("%s (params: %zu, endpoint: %s, type: %d)\n",
621                 self->token, self->parameter_count, is_endpoint, self->token_type);
622         }
623     }
624 }
625 
626 static void
s_ztrie_print_tree(ztrie_node_t * self)627 s_ztrie_print_tree (ztrie_node_t *self)
628 {
629     //  Print tree like structure
630     s_ztrie_print_tree_line (self, true);
631     ztrie_node_t *child = (ztrie_node_t *) zlistx_first (self->children);
632     while (child) {
633         s_ztrie_print_tree (child);
634         child = (ztrie_node_t *) zlistx_next (self->children);
635     }
636 }
637 
638 void
ztrie_print(ztrie_t * self)639 ztrie_print (ztrie_t *self)
640 {
641     assert (self);
642     s_ztrie_print_tree (self->root);
643 }
644 
645 
646 //  --------------------------------------------------------------------------
647 //  Self test of this class.
648 
649 void
ztrie_test(bool verbose)650 ztrie_test (bool verbose)
651 {
652     printf (" * ztrie: ");
653     //  @selftest
654     //  Create a new trie for matching strings that can be tokenized by a slash
655     //  (e.g. URLs minus the protocol, address and port).
656     ztrie_t *self = ztrie_new ('/');
657     assert (self);
658 
659     int ret = 0;
660 
661     //  Let's start by inserting a couple of routes into the trie.
662     //  This one is for the route '/foo/bar' the slash at the beginning of the
663     //  route is important because everything before the first delimiter will be
664     //  discarded. A slash at the end of a route is optional though. The data
665     //  associated with this node is passed without destroy function which means
666     //  it must be destroyed by the caller.
667     int foo_bar_data = 10;
668     ret = ztrie_insert_route (self, "/foo/bar", &foo_bar_data, NULL);
669     assert (ret == 0);
670 
671     //  Now suppose we like to match all routes with two tokens that start with
672     //  '/foo/' but aren't '/foo/bar'. This is possible by using regular
673     //  expressions which are enclosed in an opening and closing curly bracket.
674     //  Tokens that contain regular  expressions are always match after string
675     //  based tokens.
676     //  Note: There is no order in which regular expressions are sorted thus
677     //  if you enter multiple expressions for a route you will have to make
678     //  sure they don't have overlapping results. For example '/foo/{[^/]+}'
679     //  and '/foo/{\d+} having could turn out badly.
680     int foo_other_data = 100;
681     ret = ztrie_insert_route (self, "/foo/{[^/]+}", &foo_other_data, NULL);
682     assert (ret == 0);
683 
684     //  Regular expression are only matched against tokens of the same level.
685     //  This allows us to append to are route with a regular expression as if
686     //  it were a string.
687     ret = ztrie_insert_route (self, "/foo/{[^/]+}/gulp", NULL, NULL);
688     assert (ret == 0);
689 
690     //  Routes are identified by their endpoint, which is the last token of the route.
691     //  It is possible to insert routes for a node that already exists but isn't an
692     //  endpoint yet. The delimiter at the end of a route is optional and has no effect.
693     ret = ztrie_insert_route (self, "/foo/", NULL, NULL);
694     assert (ret == 0);
695 
696     //  If you try to insert a route which already exists the method will return -1.
697     ret = ztrie_insert_route (self, "/foo", NULL, NULL);
698     assert (ret == -1);
699 
700     //  It is not allowed to insert routes with empty tokens.
701     ret = ztrie_insert_route (self, "//foo", NULL, NULL);
702     assert (ret == -1);
703 
704     //  Everything before the first delimiter is ignored so 'foo/bar/baz' is equivalent
705     //  to '/bar/baz'.
706     ret = ztrie_insert_route (self, "foo/bar/baz", NULL, NULL);
707     assert (ret == 0);
708     ret = ztrie_insert_route (self, "/bar/baz", NULL, NULL);
709     assert (ret == -1);
710 
711     //  Of course you are allowed to remove routes, in case there is data associated with a
712     //  route and a destroy data function has been supplied that data will be destroyed.
713     ret = ztrie_remove_route (self, "/foo");
714     assert (ret == 0);
715 
716     //  Removing a non existent route will  as well return -1.
717     ret = ztrie_remove_route (self, "/foo");
718     assert (ret == -1);
719 
720     //  Removing a route with a regular expression must exactly match the entered one.
721     ret = ztrie_remove_route (self, "/foo/{[^/]+}");
722     assert (ret == 0);
723 
724     //  Next we like to match a path by regular expressions and also extract matched
725     //  parts of a route. This can be done by naming the regular expression. The name of a
726     //  regular expression is entered at the beginning of the curly brackets and separated
727     //  by a colon from the regular expression. The first one in this examples is named
728     //  'name' and names the expression '[^/]'. If there is no capturing group defined in
729     //  the expression the whole matched string will be associated with this parameter. In
730     //  case you don't like the get the whole matched string use a capturing group, like
731     //  it has been done for the 'id' parameter. This is nice but you can even match as
732     //  many parameter for a token as you like. Therefore simply put the parameter names
733     //  separated by colons in front of the regular expression and make sure to add a
734     //  capturing group for each parameter. The first parameter will be associated with
735     //  the first capturing and so on.
736     char *data = (char *) malloc (80);
737     sprintf (data, "%s", "Hello World!");
738     ret = ztrie_insert_route (self, "/baz/{name:[^/]+}/{id:--(\\d+)}/{street:nr:(\\a+)(\\d+)}", data, NULL);
739     assert (ret == 0);
740 
741     //  There is a lot you can do with regular expression but matching routes
742     //  of arbitrary length wont work. Therefore we make use of the asterisk
743     //  operator. Just place it at the end of your route, e.g. '/config/bar/*'.
744     ret = ztrie_insert_route (self, "/config/bar/*", NULL, NULL);
745     assert (ret == 0);
746 
747     //  Appending to an asterisk as you would to with a regular expression
748     //  isn't valid.
749     ret = ztrie_insert_route (self, "/config/bar/*/bar", NULL, NULL);
750     assert (ret == -1);
751 
752     //  The asterisk operator will only work as a leaf in the tree. If you
753     //  enter an asterisk in the middle of your route it will simply be
754     //  interpreted as a string.
755     ret = ztrie_insert_route (self, "/test/*/bar", NULL, NULL);
756     assert (ret == 0);
757 
758     //  If a parent has an asterisk as child it is not allowed to have
759     //  other siblings.
760     ret = ztrie_insert_route (self, "/config/bar/foo/glup", NULL, NULL);
761     assert (ret != 0);
762 
763     //  Test matches
764     bool hasMatch = false;
765 
766     //  The route '/bar/foo' will fail to match as this route has never been inserted.
767     hasMatch = ztrie_matches (self, "/bar/foo");
768     assert (!hasMatch);
769 
770     //  The route '/foo/bar' will match and we can obtain the data associated with it.
771     hasMatch = ztrie_matches (self, "/foo/bar");
772     assert (hasMatch);
773     int foo_bar_hit_data = *((int *) ztrie_hit_data (self));
774     assert (foo_bar_data == foo_bar_hit_data);
775 
776     //  This route is part of another but is no endpoint itself thus the matches will fail.
777     hasMatch = ztrie_matches (self, "/baz/blub");
778     assert (!hasMatch);
779 
780     //  This route will match our named regular expressions route. Thus we can extract data
781     //  from the route by their names.
782     hasMatch = ztrie_matches (self, "/baz/blub/--11/abc23");
783     assert (hasMatch);
784     char *match_data = (char *) ztrie_hit_data (self);
785     assert (streq ("Hello World!", match_data));
786     zhashx_t *parameters = ztrie_hit_parameters (self);
787     assert (zhashx_size (parameters) == 4);
788     assert (streq ("blub", (char *) zhashx_lookup (parameters, "name")));
789     assert (streq ("11", (char *) zhashx_lookup (parameters, "id")));
790     assert (streq ("abc", (char *) zhashx_lookup (parameters, "street")));
791     assert (streq ("23", (char *) zhashx_lookup (parameters, "nr")));
792     zhashx_destroy (&parameters);
793 
794     //  This will match our asterisk route '/config/bar/*'. As the result we
795     //  can obtain the asterisk matched part of the route.
796     hasMatch = ztrie_matches (self, "/config/bar/foo/bar");
797     assert (hasMatch);
798     assert (streq (ztrie_hit_asterisk_match (self), "foo/bar"));
799 
800     zstr_free (&data);
801     ztrie_destroy (&self);
802 
803 #if defined (__WINDOWS__)
804     zsys_shutdown();
805 #endif
806     //  @end
807 
808     printf ("OK\n");
809 }
810