]> jfr.im git - solanum.git/blame - ircd/irc_radixtree.c
ircd: radixtree: add irc_radixtree_foreach_start_from() which uses irc_radixtree_elem...
[solanum.git] / ircd / irc_radixtree.c
CommitLineData
8e6ba6f9
AC
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"
db891ac3 37#include "match.h"
8e6ba6f9
AC
38#include "irc_radixtree.h"
39
325cc939
AC
40rb_dlink_list radixtree_list = {NULL, NULL, 0};
41
8e6ba6f9
AC
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
56union irc_radixtree_elem;
57
58struct irc_radixtree
59{
60 void (*canonize_cb)(char *key);
61 union irc_radixtree_elem *root;
62
63 unsigned int count;
64 char *id;
325cc939
AC
65
66 rb_dlink_node node;
8e6ba6f9
AC
67};
68
69#define POINTERS_PER_NODE 16
70#define NIBBLE_VAL(key, nibnum) (((key)[(nibnum) / 2] >> ((nibnum) & 1 ? 0 : 4)) & 0xF)
71
72struct 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
84struct 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
99union 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 */
127static union irc_radixtree_elem *
128first_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
8e6ba6f9
AC
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 */
164struct irc_radixtree *
325cc939 165irc_radixtree_create(const char *name, void (*canonize_cb)(char *key))
8e6ba6f9
AC
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
325cc939
AC
173 rb_dlinkAdd(dtree, &dtree->node, &radixtree_list);
174
8e6ba6f9
AC
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 */
200void
201irc_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 */
242void
243irc_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 */
319void *
320irc_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 */
399void
400irc_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 */
439void *
440irc_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 */
467void
468irc_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/*
1b685743 539 * irc_radixtree_elem_find(struct irc_radixtree *dtree, const char *key)
8e6ba6f9
AC
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 */
554struct irc_radixtree_leaf *
1b685743 555irc_radixtree_elem_find(struct irc_radixtree *dict, const char *key)
8e6ba6f9
AC
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
0d9a72de
AC
612/*
613 * irc_radixtree_foreach_start_from(struct irc_radixtree *dtree, struct irc_radixtree_iteration_state *state, const char *key)
614 *
615 * Starts iteration from a specified key, by wrapping irc_radixtree_elem_find().
616 *
617 * Inputs:
618 * - patricia tree object
619 * - iterator
620 * - key to start from
621 *
622 * Outputs:
623 * - none
624 *
625 * Side Effects:
626 * - the iterator's state is initialized at a specific point
627 */
628void
629irc_radixtree_foreach_start_from(struct irc_radixtree *dtree, struct irc_radixtree_iteration_state *state, const char *key)
630{
631 s_assert(dtree != NULL);
632 s_assert(state != NULL);
633
634 if (key != NULL)
635 {
636 STATE_CUR(state) = NULL;
637 STATE_NEXT(state) = irc_radixtree_elem_find(dtree, key);
638
639 /* make STATE_CUR point to selected item and STATE_NEXT point to
640 * next item in the tree */
641 irc_radixtree_foreach_next(dtree, state);
642 }
643 else
644 irc_radixtree_foreach_start(dtree, state);
645}
646
8e6ba6f9
AC
647/*
648 * irc_radixtree_add(struct irc_radixtree *dtree, const char *key, void *data)
649 *
650 * Creates a new DTree node and binds data to it.
651 *
652 * Inputs:
653 * - patricia tree object
654 * - name for new DTree node
655 * - data to bind to the new DTree node
656 *
657 * Outputs:
658 * - on success, TRUE
659 * - on failure, FALSE
660 *
661 * Side Effects:
662 * - data is inserted into the DTree.
663 */
664struct irc_radixtree_leaf *
1b685743 665irc_radixtree_elem_add(struct irc_radixtree *dict, const char *key, void *data)
8e6ba6f9
AC
666{
667 char *ckey;
668
669 union irc_radixtree_elem *delem, *prev, *newnode;
670
671 union irc_radixtree_elem **place1;
672
673 int val, keylen;
674 int i, j;
675
676 s_assert(dict != NULL);
677 s_assert(key != NULL);
678 s_assert(data != NULL);
679
680 keylen = strlen(key);
681 ckey = rb_strdup(key);
682
683 if (ckey == NULL)
684 return NULL;
685
686 if (dict->canonize_cb != NULL)
687 dict->canonize_cb(ckey);
688
689 prev = NULL;
690 val = POINTERS_PER_NODE + 2; /* trap value */
691 delem = dict->root;
692
693 while (delem != NULL && !IS_LEAF(delem))
694 {
695 prev = delem;
696
697 if (delem->nibnum / 2 < keylen)
698 val = NIBBLE_VAL(ckey, delem->nibnum);
699 else
700 val = 0;
701
702 delem = delem->node.down[val];
703 }
704
705 /* Now, if the key is in the tree, delem contains it. */
706 if ((delem != NULL) && !strcmp(delem->leaf.key, ckey))
707 {
708 rb_free(ckey);
709 return NULL;
710 }
711
712 if ((delem == NULL) && (prev != NULL))
713 /* Get a leaf to compare with. */
714 delem = first_leaf(prev);
715
716 if (delem == NULL)
717 {
718 s_assert(prev == NULL);
719 s_assert(dict->count == 0);
720 place1 = &dict->root;
721 *place1 = rb_malloc(sizeof(struct irc_radixtree_leaf));
722 s_assert(*place1 != NULL);
723 (*place1)->nibnum = -1;
724 (*place1)->leaf.data = data;
725 (*place1)->leaf.key = ckey;
726 (*place1)->leaf.parent = prev;
727 (*place1)->leaf.parent_val = val;
728 dict->count++;
729 return &(*place1)->leaf;
730 }
731
732 /* Find the first nibble where they differ. */
733 for (i = 0; NIBBLE_VAL(ckey, i) == NIBBLE_VAL(delem->leaf.key, i); i++)
734 ;
735
736 /* Find where to insert the new node. */
737 while (prev != NULL && prev->nibnum > i)
738 {
739 val = prev->node.parent_val;
740 prev = prev->node.parent;
741 }
742
743 if ((prev == NULL) || (prev->nibnum < i))
744 {
745 /* Insert new node below prev */
746 newnode = rb_malloc(sizeof(struct irc_radixtree_node));
747 s_assert(newnode != NULL);
748 newnode->nibnum = i;
749 newnode->node.parent = prev;
750 newnode->node.parent_val = val;
751
752 for (j = 0; j < POINTERS_PER_NODE; j++)
753 newnode->node.down[j] = NULL;
754
755 if (prev == NULL)
756 {
757 newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = dict->root;
758
759 if (IS_LEAF(dict->root))
760 {
761 dict->root->leaf.parent = newnode;
762 dict->root->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
763 }
764 else
765 {
766 s_assert(dict->root->nibnum > i);
767 dict->root->node.parent = newnode;
768 dict->root->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
769 }
770
771 dict->root = newnode;
772 }
773 else
774 {
775 newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = prev->node.down[val];
776
777 if (IS_LEAF(prev->node.down[val]))
778 {
779 prev->node.down[val]->leaf.parent = newnode;
780 prev->node.down[val]->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
781 }
782 else
783 {
784 prev->node.down[val]->node.parent = newnode;
785 prev->node.down[val]->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
786 }
787
788 prev->node.down[val] = newnode;
789 }
790 }
791 else
792 {
793 /* This nibble is already checked. */
794 s_assert(prev->nibnum == i);
795 newnode = prev;
796 }
797
798 val = NIBBLE_VAL(ckey, i);
799 place1 = &newnode->node.down[val];
800 s_assert(*place1 == NULL);
801 *place1 = rb_malloc(sizeof(struct irc_radixtree_leaf));
802 s_assert(*place1 != NULL);
803 (*place1)->nibnum = -1;
804 (*place1)->leaf.data = data;
805 (*place1)->leaf.key = ckey;
806 (*place1)->leaf.parent = newnode;
807 (*place1)->leaf.parent_val = val;
808 dict->count++;
809 return &(*place1)->leaf;
810}
811
812int
813irc_radixtree_add(struct irc_radixtree *dict, const char *key, void *data)
814{
1b685743 815 return (irc_radixtree_elem_add(dict, key, data) != NULL);
8e6ba6f9
AC
816}
817
818/*
819 * irc_radixtree_delete(struct irc_radixtree *dtree, const char *key)
820 *
821 * Deletes data from a patricia tree.
822 *
823 * Inputs:
824 * - patricia tree object
825 * - name of DTree node to delete
826 *
827 * Outputs:
828 * - on success, the remaining data that needs to be rb_freed
829 * - on failure, NULL
830 *
831 * Side Effects:
832 * - data is removed from the DTree.
833 *
834 * Notes:
835 * - the returned data needs to be rb_freed/released manually!
836 */
837void *
838irc_radixtree_delete(struct irc_radixtree *dict, const char *key)
839{
840 void *data;
841 struct irc_radixtree_leaf *leaf;
842
1b685743 843 leaf = irc_radixtree_elem_find(dict, key);
8e6ba6f9
AC
844
845 if (leaf == NULL)
846 return NULL;
847
848 data = leaf->data;
849 irc_radixtree_elem_delete(dict, leaf);
850 return data;
851}
852
853void
854irc_radixtree_elem_delete(struct irc_radixtree *dict, struct irc_radixtree_leaf *leaf)
855{
856 union irc_radixtree_elem *delem, *prev, *next;
857
858 int val, i, used;
859
860 s_assert(dict != NULL);
861 s_assert(leaf != NULL);
862
863 delem = (union irc_radixtree_elem *) leaf;
864
865 val = delem->leaf.parent_val;
866 prev = delem->leaf.parent;
867
868 rb_free(delem->leaf.key);
869 rb_free(delem);
870
871 if (prev != NULL)
872 {
873 prev->node.down[val] = NULL;
874
875 /* Leaf is gone, now consider the node it was in. */
876 delem = prev;
877
878 used = -1;
879
880 for (i = 0; i < POINTERS_PER_NODE; i++)
881 if (delem->node.down[i] != NULL)
882 used = used == -1 ? i : -2;
883
884 s_assert(used == -2 || used >= 0);
885
886 if (used >= 0)
887 {
888 /* Only one pointer in this node, remove it.
889 * Replace the pointer that pointed to it by
890 * the sole pointer in it.
891 */
892 next = delem->node.down[used];
893 val = delem->node.parent_val;
894 prev = delem->node.parent;
895
896 if (prev != NULL)
897 prev->node.down[val] = next;
898 else
899 dict->root = next;
900
901 if (IS_LEAF(next))
902 next->leaf.parent = prev, next->leaf.parent_val = val;
903 else
904 next->node.parent = prev, next->node.parent_val = val;
905
906 rb_free(delem);
907 }
908 }
909 else
910 {
911 /* This was the last leaf. */
912 dict->root = NULL;
913 }
914
915 dict->count--;
916
917 if (dict->count == 0)
918 {
919 s_assert(dict->root == NULL);
920 dict->root = NULL;
921 }
922}
923
924/*
925 * irc_radixtree_retrieve(struct irc_radixtree *dtree, const char *key)
926 *
927 * Retrieves data from a patricia.
928 *
929 * Inputs:
930 * - patricia tree object
931 * - name of node to lookup
932 *
933 * Outputs:
934 * - on success, the data bound to the DTree node.
935 * - on failure, NULL
936 *
937 * Side Effects:
938 * - none
939 */
940void *
941irc_radixtree_retrieve(struct irc_radixtree *dtree, const char *key)
942{
1b685743 943 struct irc_radixtree_leaf *delem = irc_radixtree_elem_find(dtree, key);
8e6ba6f9
AC
944
945 if (delem != NULL)
946 return delem->data;
947
948 return NULL;
949}
950
951const char *
1b685743 952irc_radixtree_elem_get_key(struct irc_radixtree_leaf *leaf)
8e6ba6f9
AC
953{
954 s_assert(leaf != NULL);
955
956 return leaf->key;
957}
958
959void
1b685743 960irc_radixtree_elem_set_data(struct irc_radixtree_leaf *leaf, void *data)
8e6ba6f9
AC
961{
962 s_assert(leaf != NULL);
963
964 leaf->data = data;
965}
966
967void *
1b685743 968irc_radixtree_elem_get_data(struct irc_radixtree_leaf *leaf)
8e6ba6f9
AC
969{
970 s_assert(leaf != NULL);
971
972 return leaf->data;
973}
974
975/*
976 * irc_radixtree_size(struct irc_radixtree *dict)
977 *
978 * Returns the size of a patricia.
979 *
980 * Inputs:
981 * - patricia tree object
982 *
983 * Outputs:
984 * - size of patricia
985 *
986 * Side Effects:
987 * - none
988 */
989unsigned int
990irc_radixtree_size(struct irc_radixtree *dict)
991{
992 s_assert(dict != NULL);
993
994 return dict->count;
995}
996
997/* returns the sum of the depths of the subtree rooted in delem at depth depth */
998/* there is no need for this to be recursive, but it is easier... */
999static int
1000stats_recurse(union irc_radixtree_elem *delem, int depth, int *pmaxdepth)
1001{
1002 int result = 0;
1003 int val;
1004 union irc_radixtree_elem *next;
1005
1006 if (depth > *pmaxdepth)
1007 *pmaxdepth = depth;
1008
1009 if (depth == 0)
1010 {
1011 if (IS_LEAF(delem))
1012 s_assert(delem->leaf.parent == NULL);
1013
1014 else
1015 s_assert(delem->node.parent == NULL);
1016 }
1017
1018 if (IS_LEAF(delem))
1019 return depth;
1020
1021 for (val = 0; val < POINTERS_PER_NODE; val++)
1022 {
1023 next = delem->node.down[val];
1024
1025 if (next == NULL)
1026 continue;
1027
1028 result += stats_recurse(next, depth + 1, pmaxdepth);
1029
1030 if (IS_LEAF(next))
1031 {
1032 s_assert(next->leaf.parent == delem);
1033 s_assert(next->leaf.parent_val == val);
1034 }
1035 else
1036 {
1037 s_assert(next->node.parent == delem);
1038 s_assert(next->node.parent_val == val);
1039 s_assert(next->node.nibnum > delem->node.nibnum);
1040 }
1041 }
1042
1043 return result;
1044}
1045
1046/*
1047 * irc_radixtree_stats(struct irc_radixtree *dict, void (*cb)(const char *line, void *privdata), void *privdata)
1048 *
1049 * Returns the size of a patricia.
1050 *
1051 * Inputs:
1052 * - patricia tree object
1053 * - callback
1054 * - data for callback
1055 *
1056 * Outputs:
1057 * - none
1058 *
1059 * Side Effects:
1060 * - callback called with stats text
1061 */
1062void
1063irc_radixtree_stats(struct irc_radixtree *dict, void (*cb)(const char *line, void *privdata), void *privdata)
1064{
1065 char str[256];
1066 int sum, maxdepth;
1067
1068 s_assert(dict != NULL);
1069
8e6ba6f9 1070 maxdepth = 0;
8e6ba6f9
AC
1071 if (dict->count > 0)
1072 {
1073 sum = stats_recurse(dict->root, 0, &maxdepth);
8dacf9e9 1074 snprintf(str, sizeof str, "%-30s %-15s %-10d %-10d %-10d %-10d", dict->id, "RADIX", dict->count, sum, sum / dict->count, maxdepth);
8e6ba6f9
AC
1075 }
1076 else
1077 {
8dacf9e9 1078 snprintf(str, sizeof str, "%-30s %-15s %-10s %-10s %-10s %-10s", dict->id, "RADIX", "0", "0", "0", "0");
8e6ba6f9
AC
1079 }
1080
1081 cb(str, privdata);
1082 return;
1083}
325cc939
AC
1084
1085void
1086irc_radixtree_stats_walk(void (*cb)(const char *line, void *privdata), void *privdata)
1087{
1088 rb_dlink_node *ptr;
1089
1090 RB_DLINK_FOREACH(ptr, radixtree_list.head)
1091 {
1092 irc_radixtree_stats(ptr->data, cb, privdata);
1093 }
1094}
db891ac3
AC
1095
1096void irc_radixtree_irccasecanon(char *str)
1097{
1098 while (*str)
1099 {
1100 *str = ToUpper(*str);
1101 str++;
1102 }
1103 return;
1104}
1105
1106void irc_radixtree_strcasecanon(char *str)
1107{
1108 while (*str)
1109 {
1110 *str = toupper((unsigned char)*str);
1111 str++;
1112 }
1113 return;
1114}
1115