]> jfr.im git - solanum.git/blob - src/irc_dictionary.c
update NEWS
[solanum.git] / src / irc_dictionary.c
1 /*
2 * charybdis: an advanced ircd
3 * irc_dictionary.c: Dictionary-based information storage.
4 *
5 * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
6 * Copyright (c) 2007 Jilles Tjoelker <jilles -at- stack.nl>
7 *
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice is present in all copies.
11 *
12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
16 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
20 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
21 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22 * POSSIBILITY OF SUCH DAMAGE.
23 */
24
25 #include "stdinc.h"
26 #include "match.h"
27 #include "client.h"
28 #include "setup.h"
29 #include "irc_dictionary.h"
30 #include "s_assert.h"
31 #include "logger.h"
32
33 static rb_bh *elem_heap = NULL;
34
35 struct Dictionary
36 {
37 DCF compare_cb;
38 struct DictionaryElement *root, *head, *tail;
39 unsigned int count;
40 char *id;
41 unsigned int dirty:1;
42 };
43
44 /*
45 * irc_dictionary_create(DCF compare_cb)
46 *
47 * Dictionary object factory.
48 *
49 * Inputs:
50 * - function to use for comparing two entries in the dtree
51 *
52 * Outputs:
53 * - on success, a new dictionary object.
54 *
55 * Side Effects:
56 * - if services runs out of memory and cannot allocate the object,
57 * the program will abort.
58 */
59 struct Dictionary *irc_dictionary_create(DCF compare_cb)
60 {
61 struct Dictionary *dtree = (struct Dictionary *) rb_malloc(sizeof(struct Dictionary));
62
63 dtree->compare_cb = compare_cb;
64
65 if (!elem_heap)
66 elem_heap = rb_bh_create(sizeof(struct DictionaryElement), 1024, "dictionary_elem_heap");
67
68 return dtree;
69 }
70
71 /*
72 * irc_dictionary_create_named(const char *name,
73 * DCF compare_cb)
74 *
75 * Dictionary object factory.
76 *
77 * Inputs:
78 * - dictionary name
79 * - function to use for comparing two entries in the dtree
80 *
81 * Outputs:
82 * - on success, a new dictionary object.
83 *
84 * Side Effects:
85 * - if services runs out of memory and cannot allocate the object,
86 * the program will abort.
87 */
88 struct Dictionary *irc_dictionary_create_named(const char *name,
89 DCF compare_cb)
90 {
91 struct Dictionary *dtree = (struct Dictionary *) rb_malloc(sizeof(struct Dictionary));
92
93 dtree->compare_cb = compare_cb;
94 dtree->id = rb_strdup(name);
95
96 if (!elem_heap)
97 elem_heap = rb_bh_create(sizeof(struct DictionaryElement), 1024, "dictionary_elem_heap");
98
99 return dtree;
100 }
101
102 /*
103 * irc_dictionary_set_comparator_func(struct Dictionary *dict,
104 * DCF compare_cb)
105 *
106 * Resets the comparator function used by the dictionary code for
107 * updating the DTree structure.
108 *
109 * Inputs:
110 * - dictionary object
111 * - new comparator function (passed as functor)
112 *
113 * Outputs:
114 * - nothing
115 *
116 * Side Effects:
117 * - the dictionary comparator function is reset.
118 */
119 void irc_dictionary_set_comparator_func(struct Dictionary *dict,
120 DCF compare_cb)
121 {
122 s_assert(dict != NULL);
123 s_assert(compare_cb != NULL);
124
125 dict->compare_cb = compare_cb;
126 }
127
128 /*
129 * irc_dictionary_get_comparator_func(struct Dictionary *dict)
130 *
131 * Returns the current comparator function used by the dictionary.
132 *
133 * Inputs:
134 * - dictionary object
135 *
136 * Outputs:
137 * - comparator function (returned as functor)
138 *
139 * Side Effects:
140 * - none
141 */
142 DCF
143 irc_dictionary_get_comparator_func(struct Dictionary *dict)
144 {
145 s_assert(dict != NULL);
146
147 return dict->compare_cb;
148 }
149
150 /*
151 * irc_dictionary_get_linear_index(struct Dictionary *dict,
152 * const char *key)
153 *
154 * Gets a linear index number for key.
155 *
156 * Inputs:
157 * - dictionary tree object
158 * - pointer to data
159 *
160 * Outputs:
161 * - position, from zero.
162 *
163 * Side Effects:
164 * - rebuilds the linear index if the tree is marked as dirty.
165 */
166 int
167 irc_dictionary_get_linear_index(struct Dictionary *dict, const char *key)
168 {
169 struct DictionaryElement *elem;
170
171 s_assert(dict != NULL);
172 s_assert(key != NULL);
173
174 elem = irc_dictionary_find(dict, key);
175 if (elem == NULL)
176 return -1;
177
178 if (!dict->dirty)
179 return elem->position;
180 else
181 {
182 struct DictionaryElement *delem;
183 int i;
184
185 for (delem = dict->head, i = 0; delem != NULL; delem = delem->next, i++)
186 delem->position = i;
187
188 dict->dirty = FALSE;
189 }
190
191 return elem->position;
192 }
193
194 /*
195 * irc_dictionary_retune(struct Dictionary *dict, const char *key)
196 *
197 * Retunes the tree, self-optimizing for the element which belongs to key.
198 *
199 * Inputs:
200 * - node to begin search from
201 *
202 * Outputs:
203 * - none
204 *
205 * Side Effects:
206 * - a new root node is nominated.
207 */
208 static void
209 irc_dictionary_retune(struct Dictionary *dict, const char *key)
210 {
211 struct DictionaryElement n, *tn, *left, *right, *node;
212 int ret;
213
214 s_assert(dict != NULL);
215
216 if (dict->root == NULL)
217 return;
218
219 /*
220 * we initialize n with known values, since it's on stack
221 * memory. otherwise the dict would become corrupted.
222 *
223 * n is used for temporary storage while the tree is retuned.
224 * -nenolod
225 */
226 n.left = n.right = NULL;
227 left = right = &n;
228
229 /* this for(;;) loop is the main workhorse of the rebalancing */
230 for (node = dict->root; ; )
231 {
232 if ((ret = dict->compare_cb(key, node->key)) == 0)
233 break;
234
235 if (ret < 0)
236 {
237 if (node->left == NULL)
238 break;
239
240 if ((ret = dict->compare_cb(key, node->left->key)) < 0)
241 {
242 tn = node->left;
243 node->left = tn->right;
244 tn->right = node;
245 node = tn;
246
247 if (node->left == NULL)
248 break;
249 }
250
251 right->left = node;
252 right = node;
253 node = node->left;
254 }
255 else
256 {
257 if (node->right == NULL)
258 break;
259
260 if ((ret = dict->compare_cb(key, node->right->key)) > 0)
261 {
262 tn = node->right;
263 node->right = tn->left;
264 tn->left = node;
265 node = tn;
266
267 if (node->right == NULL)
268 break;
269 }
270
271 left->right = node;
272 left = node;
273 node = node->right;
274 }
275 }
276
277 left->right = node->left;
278 right->left = node->right;
279
280 node->left = n.right;
281 node->right = n.left;
282
283 dict->root = node;
284 }
285
286 /*
287 * irc_dictionary_link(struct Dictionary *dict,
288 * struct DictionaryElement *delem)
289 *
290 * Links a dictionary tree element to the dictionary.
291 *
292 * When we add new nodes to the tree, it becomes the
293 * next nominated root. This is perhaps not a wise
294 * optimization because of automatic retuning, but
295 * it keeps the code simple.
296 *
297 * Inputs:
298 * - dictionary tree
299 * - dictionary tree element
300 *
301 * Outputs:
302 * - nothing
303 *
304 * Side Effects:
305 * - a node is linked to the dictionary tree
306 */
307 static void
308 irc_dictionary_link(struct Dictionary *dict,
309 struct DictionaryElement *delem)
310 {
311 s_assert(dict != NULL);
312 s_assert(delem != NULL);
313
314 dict->dirty = TRUE;
315
316 dict->count++;
317
318 if (dict->root == NULL)
319 {
320 delem->left = delem->right = NULL;
321 delem->next = delem->prev = NULL;
322 dict->head = dict->tail = dict->root = delem;
323 }
324 else
325 {
326 int ret;
327
328 irc_dictionary_retune(dict, delem->key);
329
330 if ((ret = dict->compare_cb(delem->key, dict->root->key)) < 0)
331 {
332 delem->left = dict->root->left;
333 delem->right = dict->root;
334 dict->root->left = NULL;
335
336 if (dict->root->prev)
337 dict->root->prev->next = delem;
338 else
339 dict->head = delem;
340
341 delem->prev = dict->root->prev;
342 delem->next = dict->root;
343 dict->root->prev = delem;
344 dict->root = delem;
345 }
346 else if (ret > 0)
347 {
348 delem->right = dict->root->right;
349 delem->left = dict->root;
350 dict->root->right = NULL;
351
352 if (dict->root->next)
353 dict->root->next->prev = delem;
354 else
355 dict->tail = delem;
356
357 delem->next = dict->root->next;
358 delem->prev = dict->root;
359 dict->root->next = delem;
360 dict->root = delem;
361 }
362 else
363 {
364 dict->root->key = delem->key;
365 dict->root->data = delem->data;
366 dict->count--;
367
368 rb_bh_free(elem_heap, delem);
369 }
370 }
371 }
372
373 /*
374 * irc_dictionary_unlink_root(struct Dictionary *dict)
375 *
376 * Unlinks the root dictionary tree element from the dictionary.
377 *
378 * Inputs:
379 * - dictionary tree
380 *
381 * Outputs:
382 * - nothing
383 *
384 * Side Effects:
385 * - the root node is unlinked from the dictionary tree
386 */
387 static void
388 irc_dictionary_unlink_root(struct Dictionary *dict)
389 {
390 struct DictionaryElement *delem, *nextnode, *parentofnext;
391
392 dict->dirty = TRUE;
393
394 delem = dict->root;
395 if (delem == NULL)
396 return;
397
398 if (dict->root->left == NULL)
399 dict->root = dict->root->right;
400 else if (dict->root->right == NULL)
401 dict->root = dict->root->left;
402 else
403 {
404 /* Make the node with the next highest key the new root.
405 * This node has a NULL left pointer. */
406 nextnode = delem->next;
407 s_assert(nextnode->left == NULL);
408 if (nextnode == delem->right)
409 {
410 dict->root = nextnode;
411 dict->root->left = delem->left;
412 }
413 else
414 {
415 parentofnext = delem->right;
416 while (parentofnext->left != NULL && parentofnext->left != nextnode)
417 parentofnext = parentofnext->left;
418 s_assert(parentofnext->left == nextnode);
419 parentofnext->left = nextnode->right;
420 dict->root = nextnode;
421 dict->root->left = delem->left;
422 dict->root->right = delem->right;
423 }
424 }
425
426 /* linked list */
427 if (delem->prev != NULL)
428 delem->prev->next = delem->next;
429
430 if (dict->head == delem)
431 dict->head = delem->next;
432
433 if (delem->next)
434 delem->next->prev = delem->prev;
435
436 if (dict->tail == delem)
437 dict->tail = delem->prev;
438
439 dict->count--;
440 }
441
442 /*
443 * irc_dictionary_destroy(struct Dictionary *dtree,
444 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
445 * void *privdata);
446 *
447 * Recursively destroys all nodes in a dictionary tree.
448 *
449 * Inputs:
450 * - dictionary tree object
451 * - optional iteration callback
452 * - optional opaque/private data to pass to callback
453 *
454 * Outputs:
455 * - nothing
456 *
457 * Side Effects:
458 * - on success, a dtree and optionally it's children are destroyed.
459 *
460 * Notes:
461 * - if this is called without a callback, the objects bound to the
462 * DTree will not be destroyed.
463 */
464 void irc_dictionary_destroy(struct Dictionary *dtree,
465 void (*destroy_cb)(struct DictionaryElement *delem, void *privdata),
466 void *privdata)
467 {
468 struct DictionaryElement *n, *tn;
469
470 s_assert(dtree != NULL);
471
472 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
473 {
474 if (destroy_cb != NULL)
475 (*destroy_cb)(n, privdata);
476
477 rb_bh_free(elem_heap, n);
478 }
479
480 rb_free(dtree);
481 }
482
483 /*
484 * irc_dictionary_foreach(struct Dictionary *dtree,
485 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
486 * void *privdata);
487 *
488 * Iterates over all entries in a DTree.
489 *
490 * Inputs:
491 * - dictionary tree object
492 * - optional iteration callback
493 * - optional opaque/private data to pass to callback
494 *
495 * Outputs:
496 * - nothing
497 *
498 * Side Effects:
499 * - on success, a dtree is iterated
500 */
501 void irc_dictionary_foreach(struct Dictionary *dtree,
502 int (*foreach_cb)(struct DictionaryElement *delem, void *privdata),
503 void *privdata)
504 {
505 struct DictionaryElement *n, *tn;
506
507 s_assert(dtree != NULL);
508
509 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
510 {
511 /* delem_t is a subclass of node_t. */
512 struct DictionaryElement *delem = (struct DictionaryElement *) n;
513
514 if (foreach_cb != NULL)
515 (*foreach_cb)(delem, privdata);
516 }
517 }
518
519 /*
520 * irc_dictionary_search(struct Dictionary *dtree,
521 * void (*destroy_cb)(struct DictionaryElement *delem, void *privdata),
522 * void *privdata);
523 *
524 * Searches all entries in a DTree using a custom callback.
525 *
526 * Inputs:
527 * - dictionary tree object
528 * - optional iteration callback
529 * - optional opaque/private data to pass to callback
530 *
531 * Outputs:
532 * - on success, the requested object
533 * - on failure, NULL.
534 *
535 * Side Effects:
536 * - a dtree is iterated until the requested conditions are met
537 */
538 void *irc_dictionary_search(struct Dictionary *dtree,
539 void *(*foreach_cb)(struct DictionaryElement *delem, void *privdata),
540 void *privdata)
541 {
542 struct DictionaryElement *n, *tn;
543 void *ret = NULL;
544
545 s_assert(dtree != NULL);
546
547 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
548 {
549 /* delem_t is a subclass of node_t. */
550 struct DictionaryElement *delem = (struct DictionaryElement *) n;
551
552 if (foreach_cb != NULL)
553 ret = (*foreach_cb)(delem, privdata);
554
555 if (ret)
556 break;
557 }
558
559 return ret;
560 }
561
562 /*
563 * irc_dictionary_foreach_start(struct Dictionary *dtree,
564 * struct DictionaryIter *state);
565 *
566 * Initializes a static DTree iterator.
567 *
568 * Inputs:
569 * - dictionary tree object
570 * - static DTree iterator
571 *
572 * Outputs:
573 * - nothing
574 *
575 * Side Effects:
576 * - the static iterator, &state, is initialized.
577 */
578 void irc_dictionary_foreach_start(struct Dictionary *dtree,
579 struct DictionaryIter *state)
580 {
581 s_assert(dtree != NULL);
582 s_assert(state != NULL);
583
584 state->cur = NULL;
585 state->next = NULL;
586
587 /* find first item */
588 state->cur = dtree->head;
589
590 if (state->cur == NULL)
591 return;
592
593 /* make state->cur point to first item and state->next point to
594 * second item */
595 state->next = state->cur;
596 irc_dictionary_foreach_next(dtree, state);
597 }
598
599 /*
600 * irc_dictionary_foreach_cur(struct Dictionary *dtree,
601 * struct DictionaryIter *state);
602 *
603 * Returns the data from the current node being iterated by the
604 * static iterator.
605 *
606 * Inputs:
607 * - dictionary tree object
608 * - static DTree iterator
609 *
610 * Outputs:
611 * - reference to data in the current dtree node being iterated
612 *
613 * Side Effects:
614 * - none
615 */
616 void *irc_dictionary_foreach_cur(struct Dictionary *dtree,
617 struct DictionaryIter *state)
618 {
619 s_assert(dtree != NULL);
620 s_assert(state != NULL);
621
622 return state->cur != NULL ? state->cur->data : NULL;
623 }
624
625 /*
626 * irc_dictionary_foreach_next(struct Dictionary *dtree,
627 * struct DictionaryIter *state);
628 *
629 * Advances a static DTree iterator.
630 *
631 * Inputs:
632 * - dictionary tree object
633 * - static DTree iterator
634 *
635 * Outputs:
636 * - nothing
637 *
638 * Side Effects:
639 * - the static iterator, &state, is advanced to a new DTree node.
640 */
641 void irc_dictionary_foreach_next(struct Dictionary *dtree,
642 struct DictionaryIter *state)
643 {
644 s_assert(dtree != NULL);
645 s_assert(state != NULL);
646
647 if (state->cur == NULL)
648 {
649 ilog(L_MAIN, "irc_dictionary_foreach_next(): called again after iteration finished on dtree<%p>", (void *)dtree);
650 return;
651 }
652
653 state->cur = state->next;
654
655 if (state->next == NULL)
656 return;
657
658 state->next = state->next->next;
659 }
660
661 /*
662 * irc_dictionary_find(struct Dictionary *dtree, const char *key)
663 *
664 * Looks up a DTree node by name.
665 *
666 * Inputs:
667 * - dictionary tree object
668 * - name of node to lookup
669 *
670 * Outputs:
671 * - on success, the dtree node requested
672 * - on failure, NULL
673 *
674 * Side Effects:
675 * - none
676 */
677 struct DictionaryElement *irc_dictionary_find(struct Dictionary *dict, const char *key)
678 {
679 s_assert(dict != NULL);
680 s_assert(key != NULL);
681
682 /* retune for key, key will be the tree's root if it's available */
683 irc_dictionary_retune(dict, key);
684
685 if (dict->root && !dict->compare_cb(key, dict->root->key))
686 return dict->root;
687
688 return NULL;
689 }
690
691 /*
692 * irc_dictionary_add(struct Dictionary *dtree, const char *key, void *data)
693 *
694 * Creates a new DTree node and binds data to it.
695 *
696 * Inputs:
697 * - dictionary tree object
698 * - name for new DTree node
699 * - data to bind to the new DTree node
700 *
701 * Outputs:
702 * - on success, a new DTree node
703 * - on failure, NULL
704 *
705 * Side Effects:
706 * - data is inserted into the DTree.
707 */
708 struct DictionaryElement *irc_dictionary_add(struct Dictionary *dict, const char *key, void *data)
709 {
710 struct DictionaryElement *delem;
711
712 s_assert(dict != NULL);
713 s_assert(key != NULL);
714 s_assert(data != NULL);
715 s_assert(irc_dictionary_find(dict, key) == NULL);
716
717 delem = rb_bh_alloc(elem_heap);
718 delem->key = key;
719 delem->data = data;
720
721 /* TBD: is this needed? --nenolod */
722 if (delem->key == NULL)
723 {
724 rb_bh_free(elem_heap, delem);
725 return NULL;
726 }
727
728 irc_dictionary_link(dict, delem);
729
730 return delem;
731 }
732
733 /*
734 * irc_dictionary_delete(struct Dictionary *dtree, const char *key)
735 *
736 * Deletes data from a dictionary tree.
737 *
738 * Inputs:
739 * - dictionary tree object
740 * - name of DTree node to delete
741 *
742 * Outputs:
743 * - on success, the remaining data that needs to be mowgli_freed
744 * - on failure, NULL
745 *
746 * Side Effects:
747 * - data is removed from the DTree.
748 *
749 * Notes:
750 * - the returned data needs to be mowgli_freed/released manually!
751 */
752 void *irc_dictionary_delete(struct Dictionary *dtree, const char *key)
753 {
754 struct DictionaryElement *delem = irc_dictionary_find(dtree, key);
755 void *data;
756
757 if (delem == NULL)
758 return NULL;
759
760 data = delem->data;
761
762 irc_dictionary_unlink_root(dtree);
763 rb_bh_free(elem_heap, delem);
764
765 return data;
766 }
767
768 /*
769 * irc_dictionary_retrieve(struct Dictionary *dtree, const char *key)
770 *
771 * Retrieves data from a dictionary.
772 *
773 * Inputs:
774 * - dictionary tree object
775 * - name of node to lookup
776 *
777 * Outputs:
778 * - on success, the data bound to the DTree node.
779 * - on failure, NULL
780 *
781 * Side Effects:
782 * - none
783 */
784 void *irc_dictionary_retrieve(struct Dictionary *dtree, const char *key)
785 {
786 struct DictionaryElement *delem = irc_dictionary_find(dtree, key);
787
788 if (delem != NULL)
789 return delem->data;
790
791 return NULL;
792 }
793
794 /*
795 * irc_dictionary_size(struct Dictionary *dict)
796 *
797 * Returns the size of a dictionary.
798 *
799 * Inputs:
800 * - dictionary tree object
801 *
802 * Outputs:
803 * - size of dictionary
804 *
805 * Side Effects:
806 * - none
807 */
808 unsigned int irc_dictionary_size(struct Dictionary *dict)
809 {
810 s_assert(dict != NULL);
811
812 return dict->count;
813 }
814
815 /* returns the sum of the depths of the subtree rooted in delem at depth depth */
816 static int
817 stats_recurse(struct DictionaryElement *delem, int depth, int *pmaxdepth)
818 {
819 int result;
820
821 if (depth > *pmaxdepth)
822 *pmaxdepth = depth;
823 result = depth;
824 if (delem->left)
825 result += stats_recurse(delem->left, depth + 1, pmaxdepth);
826 if (delem->right)
827 result += stats_recurse(delem->right, depth + 1, pmaxdepth);
828 return result;
829 }
830
831 /*
832 * irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata)
833 *
834 * Returns the size of a dictionary.
835 *
836 * Inputs:
837 * - dictionary tree object
838 * - callback
839 * - data for callback
840 *
841 * Outputs:
842 * - none
843 *
844 * Side Effects:
845 * - callback called with stats text
846 */
847 void irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata)
848 {
849 char str[256];
850 int sum, maxdepth;
851
852 s_assert(dict != NULL);
853
854 if (dict->id != NULL)
855 rb_snprintf(str, sizeof str, "Dictionary stats for %s (%d)",
856 dict->id, dict->count);
857 else
858 rb_snprintf(str, sizeof str, "Dictionary stats for <%p> (%d)",
859 (void *)dict, dict->count);
860 cb(str, privdata);
861 maxdepth = 0;
862 sum = stats_recurse(dict->root, 0, &maxdepth);
863 rb_snprintf(str, sizeof str, "Depth sum %d Avg depth %d Max depth %d", sum, sum / dict->count, maxdepth);
864 cb(str, privdata);
865 return;
866 }