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