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