]> jfr.im git - solanum.git/blob - ircd/irc_radixtree.c
extensions/helpops: implement DEHELPER command
[solanum.git] / ircd / irc_radixtree.c
1 /*
2 * charybdis: an advanced ircd.
3 * irc_radixtree.c: Dictionary-based information storage.
4 *
5 * Copyright (c) 2007-2016 William Pitcock <nenolod -at- dereferenced.org>
6 * Copyright (c) 2007-2016 Jilles Tjoelker <jilles -at- stack.nl>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * 1. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include "stdinc.h"
36 #include "s_assert.h"
37 #include "match.h"
38 #include "irc_radixtree.h"
39
40 rb_dlink_list radixtree_list = {NULL, NULL, 0};
41
42 /*
43 * Patricia tree.
44 *
45 * A radix trie that avoids one-way branching and redundant nodes.
46 *
47 * To find a node, the tree is traversed starting from the root. The
48 * nibnum in each node indicates which nibble of the key needs to be
49 * tested, and the appropriate branch is taken.
50 *
51 * The nibnum values are strictly increasing while going down the tree.
52 *
53 * -- jilles
54 */
55
56 union irc_radixtree_elem;
57
58 struct irc_radixtree
59 {
60 void (*canonize_cb)(char *key);
61 union irc_radixtree_elem *root;
62
63 unsigned int count;
64 char *id;
65
66 rb_dlink_node node;
67 };
68
69 #define POINTERS_PER_NODE 16
70 #define NIBBLE_VAL(key, nibnum) (((key)[(nibnum) / 2] >> ((nibnum) & 1 ? 0 : 4)) & 0xF)
71
72 struct irc_radixtree_node
73 {
74 /* nibble to test (nibble NUM%2 of byte NUM/2) */
75 int nibnum;
76
77 /* branches of the tree */
78 union irc_radixtree_elem *down[POINTERS_PER_NODE];
79 union irc_radixtree_elem *parent;
80
81 char parent_val;
82 };
83
84 struct irc_radixtree_leaf
85 {
86 /* -1 to indicate this is a leaf, not a node */
87 int nibnum;
88
89 /* data associated with the key */
90 void *data;
91
92 /* key (canonized copy) */
93 char *key;
94 union irc_radixtree_elem *parent;
95
96 char parent_val;
97 };
98
99 union irc_radixtree_elem
100 {
101 int nibnum;
102 struct irc_radixtree_node node;
103
104 struct irc_radixtree_leaf leaf;
105 };
106
107 #define IS_LEAF(elem) ((elem)->nibnum == -1)
108
109 /* Preserve compatibility with the old mowgli_patricia.h */
110 #define STATE_CUR(state) ((state)->pspare[0])
111 #define STATE_NEXT(state) ((state)->pspare[1])
112
113 /*
114 * first_leaf()
115 *
116 * Find the smallest leaf hanging off a subtree.
117 *
118 * Inputs:
119 * - element (may be leaf or node) heading subtree
120 *
121 * Outputs:
122 * - lowest leaf in subtree
123 *
124 * Side Effects:
125 * - none
126 */
127 static union irc_radixtree_elem *
128 first_leaf(union irc_radixtree_elem *delem)
129 {
130 int val;
131
132 while (!IS_LEAF(delem))
133 {
134 for (val = 0; val < POINTERS_PER_NODE; val++)
135 if (delem->node.down[val] != NULL)
136 {
137 delem = delem->node.down[val];
138 break;
139 }
140 }
141
142 return delem;
143 }
144
145 /*
146 * irc_radixtree_create_named(const char *name,
147 * void (*canonize_cb)(char *key))
148 *
149 * Dictionary object factory.
150 *
151 * Inputs:
152 * - patricia name
153 * - function to use for canonizing keys (for example, use
154 * a function that makes the string upper case to create
155 * a patricia with case-insensitive matching)
156 *
157 * Outputs:
158 * - on success, a new patricia object.
159 *
160 * Side Effects:
161 * - if services runs out of memory and cannot allocate the object,
162 * the program will abort.
163 */
164 struct irc_radixtree *
165 irc_radixtree_create(const char *name, void (*canonize_cb)(char *key))
166 {
167 struct irc_radixtree *dtree = (struct irc_radixtree *) rb_malloc(sizeof(struct irc_radixtree));
168
169 dtree->canonize_cb = canonize_cb;
170 dtree->id = rb_strdup(name);
171 dtree->root = NULL;
172
173 rb_dlinkAdd(dtree, &dtree->node, &radixtree_list);
174
175 return dtree;
176 }
177
178 /*
179 * irc_radixtree_destroy(struct irc_radixtree *dtree,
180 * void (*destroy_cb)(const char *key, void *data, void *privdata),
181 * void *privdata);
182 *
183 * Recursively destroys all nodes in a patricia tree.
184 *
185 * Inputs:
186 * - patricia tree object
187 * - optional iteration callback
188 * - optional opaque/private data to pass to callback
189 *
190 * Outputs:
191 * - nothing
192 *
193 * Side Effects:
194 * - on success, a dtree and optionally it's children are destroyed.
195 *
196 * Notes:
197 * - if this is called without a callback, the objects bound to the
198 * DTree will not be destroyed.
199 */
200 void
201 irc_radixtree_destroy(struct irc_radixtree *dtree, void (*destroy_cb)(const char *key, void *data, void *privdata), void *privdata)
202 {
203 struct irc_radixtree_iteration_state state;
204 union irc_radixtree_elem *delem;
205
206 void *entry;
207
208 s_assert(dtree != NULL);
209
210 IRC_RADIXTREE_FOREACH(entry, &state, dtree)
211 {
212 delem = STATE_CUR(&state);
213
214 if (destroy_cb != NULL)
215 (*destroy_cb)(delem->leaf.key, delem->leaf.data,
216 privdata);
217
218 irc_radixtree_delete(dtree, delem->leaf.key);
219 }
220
221 rb_free(dtree);
222 }
223
224 /*
225 * irc_radixtree_foreach(struct irc_radixtree *dtree,
226 * int (*foreach_cb)(const char *key, void *data, void *privdata),
227 * void *privdata);
228 *
229 * Iterates over all entries in a DTree.
230 *
231 * Inputs:
232 * - patricia tree object
233 * - optional iteration callback
234 * - optional opaque/private data to pass to callback
235 *
236 * Outputs:
237 * - nothing
238 *
239 * Side Effects:
240 * - on success, a dtree is iterated
241 */
242 void
243 irc_radixtree_foreach(struct irc_radixtree *dtree, int (*foreach_cb)(const char *key, void *data, void *privdata), void *privdata)
244 {
245 union irc_radixtree_elem *delem, *next;
246
247 int val;
248
249 s_assert(dtree != NULL);
250
251 delem = dtree->root;
252
253 if (delem == NULL)
254 return;
255
256 /* Only one element in the tree */
257 if (IS_LEAF(delem))
258 {
259 if (foreach_cb != NULL)
260 (*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);
261
262 return;
263 }
264
265 val = 0;
266
267 do
268 {
269 do
270 next = delem->node.down[val++];
271 while (next == NULL && val < POINTERS_PER_NODE);
272
273 if (next != NULL)
274 {
275 if (IS_LEAF(next))
276 {
277 if (foreach_cb != NULL)
278 (*foreach_cb)(next->leaf.key, next->leaf.data, privdata);
279 }
280 else
281 {
282 delem = next;
283 val = 0;
284 }
285 }
286
287 while (val >= POINTERS_PER_NODE)
288 {
289 val = delem->node.parent_val;
290 delem = delem->node.parent;
291
292 if (delem == NULL)
293 break;
294
295 val++;
296 }
297 } while (delem != NULL);
298 }
299
300 /*
301 * irc_radixtree_search(struct irc_radixtree *dtree,
302 * void *(*foreach_cb)(const char *key, void *data, void *privdata),
303 * void *privdata);
304 *
305 * Searches all entries in a DTree using a custom callback.
306 *
307 * Inputs:
308 * - patricia tree object
309 * - optional iteration callback
310 * - optional opaque/private data to pass to callback
311 *
312 * Outputs:
313 * - on success, the requested object
314 * - on failure, NULL.
315 *
316 * Side Effects:
317 * - a dtree is iterated until the requested conditions are met
318 */
319 void *
320 irc_radixtree_search(struct irc_radixtree *dtree, void *(*foreach_cb)(const char *key, void *data, void *privdata), void *privdata)
321 {
322 union irc_radixtree_elem *delem, *next;
323
324 int val;
325 void *ret = NULL;
326
327 s_assert(dtree != NULL);
328
329 delem = dtree->root;
330
331 if (delem == NULL)
332 return NULL;
333
334 /* Only one element in the tree */
335 if (IS_LEAF(delem))
336 {
337 if (foreach_cb != NULL)
338 return (*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);
339
340 return NULL;
341 }
342
343 val = 0;
344
345 for (;;)
346 {
347 do
348 next = delem->node.down[val++];
349 while (next == NULL && val < POINTERS_PER_NODE);
350
351 if (next != NULL)
352 {
353 if (IS_LEAF(next))
354 {
355 if (foreach_cb != NULL)
356 ret = (*foreach_cb)(next->leaf.key, next->leaf.data, privdata);
357
358 if (ret != NULL)
359 break;
360 }
361 else
362 {
363 delem = next;
364 val = 0;
365 }
366 }
367
368 while (val >= POINTERS_PER_NODE)
369 {
370 val = delem->node.parent_val;
371 delem = delem->node.parent;
372
373 if (delem == NULL)
374 break;
375
376 val++;
377 }
378 }
379
380 return ret;
381 }
382
383 /*
384 * irc_radixtree_foreach_start(struct irc_radixtree *dtree,
385 * struct irc_radixtree_iteration_state *state);
386 *
387 * Initializes a static DTree iterator.
388 *
389 * Inputs:
390 * - patricia tree object
391 * - static DTree iterator
392 *
393 * Outputs:
394 * - nothing
395 *
396 * Side Effects:
397 * - the static iterator, &state, is initialized.
398 */
399 void
400 irc_radixtree_foreach_start(struct irc_radixtree *dtree, struct irc_radixtree_iteration_state *state)
401 {
402 if (dtree == NULL)
403 return;
404
405 s_assert(state != NULL);
406
407 if (dtree->root != NULL)
408 STATE_NEXT(state) = first_leaf(dtree->root);
409 else
410 STATE_NEXT(state) = NULL;
411
412 STATE_CUR(state) = STATE_NEXT(state);
413
414 if (STATE_NEXT(state) == NULL)
415 return;
416
417 /* make STATE_CUR point to first item and STATE_NEXT point to
418 * second item */
419 irc_radixtree_foreach_next(dtree, state);
420 }
421
422 /*
423 * irc_radixtree_foreach_cur(struct irc_radixtree *dtree,
424 * struct irc_radixtree_iteration_state *state);
425 *
426 * Returns the data from the current node being iterated by the
427 * static iterator.
428 *
429 * Inputs:
430 * - patricia tree object
431 * - static DTree iterator
432 *
433 * Outputs:
434 * - reference to data in the current dtree node being iterated
435 *
436 * Side Effects:
437 * - none
438 */
439 void *
440 irc_radixtree_foreach_cur(struct irc_radixtree *dtree, struct irc_radixtree_iteration_state *state)
441 {
442 if (dtree == NULL)
443 return NULL;
444
445 s_assert(state != NULL);
446
447 return STATE_CUR(state) != NULL ?
448 ((struct irc_radixtree_leaf *) STATE_CUR(state))->data : NULL;
449 }
450
451 /*
452 * irc_radixtree_foreach_next(struct irc_radixtree *dtree,
453 * struct irc_radixtree_iteration_state *state);
454 *
455 * Advances a static DTree iterator.
456 *
457 * Inputs:
458 * - patricia tree object
459 * - static DTree iterator
460 *
461 * Outputs:
462 * - nothing
463 *
464 * Side Effects:
465 * - the static iterator, &state, is advanced to a new DTree node.
466 */
467 void
468 irc_radixtree_foreach_next(struct irc_radixtree *dtree, struct irc_radixtree_iteration_state *state)
469 {
470 struct irc_radixtree_leaf *leaf;
471
472 union irc_radixtree_elem *delem, *next;
473
474 int val;
475
476 if (dtree == NULL)
477 return;
478
479 s_assert(state != NULL);
480
481 if (STATE_CUR(state) == NULL)
482 return;
483
484 STATE_CUR(state) = STATE_NEXT(state);
485
486 if (STATE_NEXT(state) == NULL)
487 return;
488
489 leaf = STATE_NEXT(state);
490 delem = leaf->parent;
491 val = leaf->parent_val;
492
493 while (delem != NULL)
494 {
495 do
496 next = delem->node.down[val++];
497 while (next == NULL && val < POINTERS_PER_NODE);
498
499 if (next != NULL)
500 {
501 if (IS_LEAF(next))
502 {
503 /* We will find the original leaf first. */
504 if (&next->leaf != leaf)
505 {
506 if (strcmp(next->leaf.key, leaf->key) < 0)
507 {
508 STATE_NEXT(state) = NULL;
509 return;
510 }
511
512 STATE_NEXT(state) = next;
513 return;
514 }
515 }
516 else
517 {
518 delem = next;
519 val = 0;
520 }
521 }
522
523 while (val >= POINTERS_PER_NODE)
524 {
525 val = delem->node.parent_val;
526 delem = delem->node.parent;
527
528 if (delem == NULL)
529 break;
530
531 val++;
532 }
533 }
534
535 STATE_NEXT(state) = NULL;
536 }
537
538 /*
539 * mowgli_radix_elem_find(struct irc_radixtree *dtree, const char *key)
540 *
541 * Looks up a DTree node by name.
542 *
543 * Inputs:
544 * - patricia tree object
545 * - name of node to lookup
546 *
547 * Outputs:
548 * - on success, the dtree node requested
549 * - on failure, NULL
550 *
551 * Side Effects:
552 * - none
553 */
554 struct irc_radixtree_leaf *
555 mowgli_radix_elem_find(struct irc_radixtree *dict, const char *key)
556 {
557 char ckey_store[256];
558
559 char *ckey_buf = NULL;
560 const char *ckey;
561 union irc_radixtree_elem *delem;
562
563 int val, keylen;
564
565 s_assert(dict != NULL);
566 s_assert(key != NULL);
567
568 keylen = strlen(key);
569
570 if (dict->canonize_cb == NULL)
571 {
572 ckey = key;
573 }
574 else
575 {
576 if (keylen >= (int) sizeof(ckey_store))
577 {
578 ckey_buf = rb_strdup(key);
579 dict->canonize_cb(ckey_buf);
580 ckey = ckey_buf;
581 }
582 else
583 {
584 rb_strlcpy(ckey_store, key, sizeof ckey_store);
585 dict->canonize_cb(ckey_store);
586 ckey = ckey_store;
587 }
588 }
589
590 delem = dict->root;
591
592 while (delem != NULL && !IS_LEAF(delem))
593 {
594 if (delem->nibnum / 2 < keylen)
595 val = NIBBLE_VAL(ckey, delem->nibnum);
596 else
597 val = 0;
598
599 delem = delem->node.down[val];
600 }
601
602 /* Now, if the key is in the tree, delem contains it. */
603 if ((delem != NULL) && strcmp(delem->leaf.key, ckey))
604 delem = NULL;
605
606 if (ckey_buf != NULL)
607 rb_free(ckey_buf);
608
609 return &delem->leaf;
610 }
611
612 /*
613 * irc_radixtree_add(struct irc_radixtree *dtree, const char *key, void *data)
614 *
615 * Creates a new DTree node and binds data to it.
616 *
617 * Inputs:
618 * - patricia tree object
619 * - name for new DTree node
620 * - data to bind to the new DTree node
621 *
622 * Outputs:
623 * - on success, TRUE
624 * - on failure, FALSE
625 *
626 * Side Effects:
627 * - data is inserted into the DTree.
628 */
629 struct irc_radixtree_leaf *
630 mowgli_radix_elem_add(struct irc_radixtree *dict, const char *key, void *data)
631 {
632 char *ckey;
633
634 union irc_radixtree_elem *delem, *prev, *newnode;
635
636 union irc_radixtree_elem **place1;
637
638 int val, keylen;
639 int i, j;
640
641 s_assert(dict != NULL);
642 s_assert(key != NULL);
643 s_assert(data != NULL);
644
645 keylen = strlen(key);
646 ckey = rb_strdup(key);
647
648 if (ckey == NULL)
649 return NULL;
650
651 if (dict->canonize_cb != NULL)
652 dict->canonize_cb(ckey);
653
654 prev = NULL;
655 val = POINTERS_PER_NODE + 2; /* trap value */
656 delem = dict->root;
657
658 while (delem != NULL && !IS_LEAF(delem))
659 {
660 prev = delem;
661
662 if (delem->nibnum / 2 < keylen)
663 val = NIBBLE_VAL(ckey, delem->nibnum);
664 else
665 val = 0;
666
667 delem = delem->node.down[val];
668 }
669
670 /* Now, if the key is in the tree, delem contains it. */
671 if ((delem != NULL) && !strcmp(delem->leaf.key, ckey))
672 {
673 rb_free(ckey);
674 return NULL;
675 }
676
677 if ((delem == NULL) && (prev != NULL))
678 /* Get a leaf to compare with. */
679 delem = first_leaf(prev);
680
681 if (delem == NULL)
682 {
683 s_assert(prev == NULL);
684 s_assert(dict->count == 0);
685 place1 = &dict->root;
686 *place1 = rb_malloc(sizeof(struct irc_radixtree_leaf));
687 s_assert(*place1 != NULL);
688 (*place1)->nibnum = -1;
689 (*place1)->leaf.data = data;
690 (*place1)->leaf.key = ckey;
691 (*place1)->leaf.parent = prev;
692 (*place1)->leaf.parent_val = val;
693 dict->count++;
694 return &(*place1)->leaf;
695 }
696
697 /* Find the first nibble where they differ. */
698 for (i = 0; NIBBLE_VAL(ckey, i) == NIBBLE_VAL(delem->leaf.key, i); i++)
699 ;
700
701 /* Find where to insert the new node. */
702 while (prev != NULL && prev->nibnum > i)
703 {
704 val = prev->node.parent_val;
705 prev = prev->node.parent;
706 }
707
708 if ((prev == NULL) || (prev->nibnum < i))
709 {
710 /* Insert new node below prev */
711 newnode = rb_malloc(sizeof(struct irc_radixtree_node));
712 s_assert(newnode != NULL);
713 newnode->nibnum = i;
714 newnode->node.parent = prev;
715 newnode->node.parent_val = val;
716
717 for (j = 0; j < POINTERS_PER_NODE; j++)
718 newnode->node.down[j] = NULL;
719
720 if (prev == NULL)
721 {
722 newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = dict->root;
723
724 if (IS_LEAF(dict->root))
725 {
726 dict->root->leaf.parent = newnode;
727 dict->root->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
728 }
729 else
730 {
731 s_assert(dict->root->nibnum > i);
732 dict->root->node.parent = newnode;
733 dict->root->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
734 }
735
736 dict->root = newnode;
737 }
738 else
739 {
740 newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = prev->node.down[val];
741
742 if (IS_LEAF(prev->node.down[val]))
743 {
744 prev->node.down[val]->leaf.parent = newnode;
745 prev->node.down[val]->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
746 }
747 else
748 {
749 prev->node.down[val]->node.parent = newnode;
750 prev->node.down[val]->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
751 }
752
753 prev->node.down[val] = newnode;
754 }
755 }
756 else
757 {
758 /* This nibble is already checked. */
759 s_assert(prev->nibnum == i);
760 newnode = prev;
761 }
762
763 val = NIBBLE_VAL(ckey, i);
764 place1 = &newnode->node.down[val];
765 s_assert(*place1 == NULL);
766 *place1 = rb_malloc(sizeof(struct irc_radixtree_leaf));
767 s_assert(*place1 != NULL);
768 (*place1)->nibnum = -1;
769 (*place1)->leaf.data = data;
770 (*place1)->leaf.key = ckey;
771 (*place1)->leaf.parent = newnode;
772 (*place1)->leaf.parent_val = val;
773 dict->count++;
774 return &(*place1)->leaf;
775 }
776
777 int
778 irc_radixtree_add(struct irc_radixtree *dict, const char *key, void *data)
779 {
780 return (mowgli_radix_elem_add(dict, key, data) != NULL);
781 }
782
783 /*
784 * irc_radixtree_delete(struct irc_radixtree *dtree, const char *key)
785 *
786 * Deletes data from a patricia tree.
787 *
788 * Inputs:
789 * - patricia tree object
790 * - name of DTree node to delete
791 *
792 * Outputs:
793 * - on success, the remaining data that needs to be rb_freed
794 * - on failure, NULL
795 *
796 * Side Effects:
797 * - data is removed from the DTree.
798 *
799 * Notes:
800 * - the returned data needs to be rb_freed/released manually!
801 */
802 void *
803 irc_radixtree_delete(struct irc_radixtree *dict, const char *key)
804 {
805 void *data;
806 struct irc_radixtree_leaf *leaf;
807
808 leaf = mowgli_radix_elem_find(dict, key);
809
810 if (leaf == NULL)
811 return NULL;
812
813 data = leaf->data;
814 irc_radixtree_elem_delete(dict, leaf);
815 return data;
816 }
817
818 void
819 irc_radixtree_elem_delete(struct irc_radixtree *dict, struct irc_radixtree_leaf *leaf)
820 {
821 union irc_radixtree_elem *delem, *prev, *next;
822
823 int val, i, used;
824
825 s_assert(dict != NULL);
826 s_assert(leaf != NULL);
827
828 delem = (union irc_radixtree_elem *) leaf;
829
830 val = delem->leaf.parent_val;
831 prev = delem->leaf.parent;
832
833 rb_free(delem->leaf.key);
834 rb_free(delem);
835
836 if (prev != NULL)
837 {
838 prev->node.down[val] = NULL;
839
840 /* Leaf is gone, now consider the node it was in. */
841 delem = prev;
842
843 used = -1;
844
845 for (i = 0; i < POINTERS_PER_NODE; i++)
846 if (delem->node.down[i] != NULL)
847 used = used == -1 ? i : -2;
848
849 s_assert(used == -2 || used >= 0);
850
851 if (used >= 0)
852 {
853 /* Only one pointer in this node, remove it.
854 * Replace the pointer that pointed to it by
855 * the sole pointer in it.
856 */
857 next = delem->node.down[used];
858 val = delem->node.parent_val;
859 prev = delem->node.parent;
860
861 if (prev != NULL)
862 prev->node.down[val] = next;
863 else
864 dict->root = next;
865
866 if (IS_LEAF(next))
867 next->leaf.parent = prev, next->leaf.parent_val = val;
868 else
869 next->node.parent = prev, next->node.parent_val = val;
870
871 rb_free(delem);
872 }
873 }
874 else
875 {
876 /* This was the last leaf. */
877 dict->root = NULL;
878 }
879
880 dict->count--;
881
882 if (dict->count == 0)
883 {
884 s_assert(dict->root == NULL);
885 dict->root = NULL;
886 }
887 }
888
889 /*
890 * irc_radixtree_retrieve(struct irc_radixtree *dtree, const char *key)
891 *
892 * Retrieves data from a patricia.
893 *
894 * Inputs:
895 * - patricia tree object
896 * - name of node to lookup
897 *
898 * Outputs:
899 * - on success, the data bound to the DTree node.
900 * - on failure, NULL
901 *
902 * Side Effects:
903 * - none
904 */
905 void *
906 irc_radixtree_retrieve(struct irc_radixtree *dtree, const char *key)
907 {
908 struct irc_radixtree_leaf *delem = mowgli_radix_elem_find(dtree, key);
909
910 if (delem != NULL)
911 return delem->data;
912
913 return NULL;
914 }
915
916 const char *
917 mowgli_radix_elem_get_key(struct irc_radixtree_leaf *leaf)
918 {
919 s_assert(leaf != NULL);
920
921 return leaf->key;
922 }
923
924 void
925 mowgli_radix_elem_set_data(struct irc_radixtree_leaf *leaf, void *data)
926 {
927 s_assert(leaf != NULL);
928
929 leaf->data = data;
930 }
931
932 void *
933 mowgli_radix_elem_get_data(struct irc_radixtree_leaf *leaf)
934 {
935 s_assert(leaf != NULL);
936
937 return leaf->data;
938 }
939
940 /*
941 * irc_radixtree_size(struct irc_radixtree *dict)
942 *
943 * Returns the size of a patricia.
944 *
945 * Inputs:
946 * - patricia tree object
947 *
948 * Outputs:
949 * - size of patricia
950 *
951 * Side Effects:
952 * - none
953 */
954 unsigned int
955 irc_radixtree_size(struct irc_radixtree *dict)
956 {
957 s_assert(dict != NULL);
958
959 return dict->count;
960 }
961
962 /* returns the sum of the depths of the subtree rooted in delem at depth depth */
963 /* there is no need for this to be recursive, but it is easier... */
964 static int
965 stats_recurse(union irc_radixtree_elem *delem, int depth, int *pmaxdepth)
966 {
967 int result = 0;
968 int val;
969 union irc_radixtree_elem *next;
970
971 if (depth > *pmaxdepth)
972 *pmaxdepth = depth;
973
974 if (depth == 0)
975 {
976 if (IS_LEAF(delem))
977 s_assert(delem->leaf.parent == NULL);
978
979 else
980 s_assert(delem->node.parent == NULL);
981 }
982
983 if (IS_LEAF(delem))
984 return depth;
985
986 for (val = 0; val < POINTERS_PER_NODE; val++)
987 {
988 next = delem->node.down[val];
989
990 if (next == NULL)
991 continue;
992
993 result += stats_recurse(next, depth + 1, pmaxdepth);
994
995 if (IS_LEAF(next))
996 {
997 s_assert(next->leaf.parent == delem);
998 s_assert(next->leaf.parent_val == val);
999 }
1000 else
1001 {
1002 s_assert(next->node.parent == delem);
1003 s_assert(next->node.parent_val == val);
1004 s_assert(next->node.nibnum > delem->node.nibnum);
1005 }
1006 }
1007
1008 return result;
1009 }
1010
1011 /*
1012 * irc_radixtree_stats(struct irc_radixtree *dict, void (*cb)(const char *line, void *privdata), void *privdata)
1013 *
1014 * Returns the size of a patricia.
1015 *
1016 * Inputs:
1017 * - patricia tree object
1018 * - callback
1019 * - data for callback
1020 *
1021 * Outputs:
1022 * - none
1023 *
1024 * Side Effects:
1025 * - callback called with stats text
1026 */
1027 void
1028 irc_radixtree_stats(struct irc_radixtree *dict, void (*cb)(const char *line, void *privdata), void *privdata)
1029 {
1030 char str[256];
1031 int sum, maxdepth;
1032
1033 s_assert(dict != NULL);
1034
1035 maxdepth = 0;
1036 if (dict->count > 0)
1037 {
1038 sum = stats_recurse(dict->root, 0, &maxdepth);
1039 snprintf(str, sizeof str, "%-30s %-15s %-10d %-10d %-10d %-10d", dict->id, "RADIX", dict->count, sum, sum / dict->count, maxdepth);
1040 }
1041 else
1042 {
1043 snprintf(str, sizeof str, "%-30s %-15s %-10s %-10s %-10s %-10s", dict->id, "RADIX", "0", "0", "0", "0");
1044 }
1045
1046 cb(str, privdata);
1047 return;
1048 }
1049
1050 void
1051 irc_radixtree_stats_walk(void (*cb)(const char *line, void *privdata), void *privdata)
1052 {
1053 rb_dlink_node *ptr;
1054
1055 RB_DLINK_FOREACH(ptr, radixtree_list.head)
1056 {
1057 irc_radixtree_stats(ptr->data, cb, privdata);
1058 }
1059 }
1060
1061 void irc_radixtree_irccasecanon(char *str)
1062 {
1063 while (*str)
1064 {
1065 *str = ToUpper(*str);
1066 str++;
1067 }
1068 return;
1069 }
1070
1071 void irc_radixtree_strcasecanon(char *str)
1072 {
1073 while (*str)
1074 {
1075 *str = toupper((unsigned char)*str);
1076 str++;
1077 }
1078 return;
1079 }
1080