]>
jfr.im git - solanum.git/blob - src/irc_dictionary.c
b393e54b126a1f65dcf422b50b7b04ef1b7a1de4
2 * charybdis: an advanced ircd
3 * irc_dictionary.c: Dictionary-based information storage.
5 * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
6 * Copyright (c) 2007 Jilles Tjoelker <jilles -at- stack.nl>
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.
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.
26 #include "sprintf_irc.h"
28 #include "irc_string.h"
33 #include "irc_dictionary.h"
35 static BlockHeap
*elem_heap
= NULL
;
40 struct DictionaryElement
*root
, *head
, *tail
;
47 * irc_dictionary_create(DCF compare_cb)
49 * Dictionary object factory.
52 * - function to use for comparing two entries in the dtree
55 * - on success, a new dictionary object.
58 * - if services runs out of memory and cannot allocate the object,
59 * the program will abort.
61 struct Dictionary
*irc_dictionary_create(DCF compare_cb
)
63 struct Dictionary
*dtree
= (struct Dictionary
*) MyMalloc(sizeof(struct Dictionary
));
65 dtree
->compare_cb
= compare_cb
;
68 elem_heap
= BlockHeapCreate(sizeof(struct DictionaryElement
), 1024);
74 * irc_dictionary_create_named(const char *name,
77 * Dictionary object factory.
81 * - function to use for comparing two entries in the dtree
84 * - on success, a new dictionary object.
87 * - if services runs out of memory and cannot allocate the object,
88 * the program will abort.
90 struct Dictionary
*irc_dictionary_create_named(const char *name
,
93 struct Dictionary
*dtree
= (struct Dictionary
*) MyMalloc(sizeof(struct Dictionary
));
95 dtree
->compare_cb
= compare_cb
;
96 DupString(dtree
->id
, name
);
99 elem_heap
= BlockHeapCreate(sizeof(struct DictionaryElement
), 1024);
105 * irc_dictionary_set_comparator_func(struct Dictionary *dict,
108 * Resets the comparator function used by the dictionary code for
109 * updating the DTree structure.
112 * - dictionary object
113 * - new comparator function (passed as functor)
119 * - the dictionary comparator function is reset.
121 void irc_dictionary_set_comparator_func(struct Dictionary
*dict
,
124 s_assert(dict
!= NULL
);
125 s_assert(compare_cb
!= NULL
);
127 dict
->compare_cb
= compare_cb
;
131 * irc_dictionary_get_comparator_func(struct Dictionary *dict)
133 * Returns the current comparator function used by the dictionary.
136 * - dictionary object
139 * - comparator function (returned as functor)
145 irc_dictionary_get_comparator_func(struct Dictionary
*dict
)
147 s_assert(dict
!= NULL
);
149 return dict
->compare_cb
;
153 * irc_dictionary_get_linear_index(struct Dictionary *dict,
156 * Gets a linear index number for key.
159 * - dictionary tree object
163 * - position, from zero.
166 * - rebuilds the linear index if the tree is marked as dirty.
169 irc_dictionary_get_linear_index(struct Dictionary
*dict
, const char *key
)
171 struct DictionaryElement
*elem
;
173 s_assert(dict
!= NULL
);
174 s_assert(key
!= NULL
);
176 elem
= irc_dictionary_find(dict
, key
);
181 return elem
->position
;
184 struct DictionaryElement
*delem
;
187 for (delem
= dict
->head
, i
= 0; delem
!= NULL
; delem
= delem
->next
, i
++)
193 return elem
->position
;
197 * irc_dictionary_retune(struct Dictionary *dict, const char *key)
199 * Retunes the tree, self-optimizing for the element which belongs to key.
201 * Tuning the tree structure is a very complex operation. Unlike
202 * 2-3-4 trees and BTree/BTree+ structures, this structure is a
203 * constantly evolving algorithm.
205 * Instead of maintaining a balanced tree, we constantly adapt the
206 * tree by nominating a new root nearby the most recently looked up
207 * or added data. We are constantly retuning ourselves instead of
208 * doing massive O(n) rebalance operations as seen in BTrees,
209 * and the level of data stored in a tree is dynamic, instead of being
210 * held to a restricted design like other trees.
212 * Moreover, we are different than a radix/patricia tree, because we
213 * don't statically allocate positions. Radix trees have the advantage
214 * of not requiring tuning or balancing operations while having the
215 * disadvantage of requiring a large amount of memory to store
216 * large trees. Our efficiency as far as speed goes is not as
217 * fast as a radix tree; but is close to it.
219 * The retuning algorithm uses the comparison callback that is
220 * passed in the initialization of the tree container. If the
221 * comparator returns a value which is less than zero, we push the
222 * losing node out of the way, causing it to later be reparented
223 * with another node. The winning child of this comparison is always
224 * the right-most node.
226 * Once we have reached the key which has been targeted, or have reached
227 * a deadend, we nominate the nearest node as the new root of the tree.
228 * If an exact match has been found, the new root becomes the node which
231 * This results in a tree which can self-optimize for both critical
232 * conditions: nodes which are distant and similar and trees which
233 * have ordered lookups.
236 * - node to begin search from
242 * - a new root node is nominated.
245 irc_dictionary_retune(struct Dictionary
*dict
, const char *key
)
247 struct DictionaryElement n
, *tn
, *left
, *right
, *node
;
250 s_assert(dict
!= NULL
);
252 if (dict
->root
== NULL
)
256 * we initialize n with known values, since it's on stack
257 * memory. otherwise the dict would become corrupted.
259 * n is used for temporary storage while the tree is retuned.
262 n
.left
= n
.right
= NULL
;
265 /* this for(;;) loop is the main workhorse of the rebalancing */
266 for (node
= dict
->root
; ; )
268 if ((ret
= dict
->compare_cb(key
, node
->key
)) == 0)
273 if (node
->left
== NULL
)
276 if ((ret
= dict
->compare_cb(key
, node
->left
->key
)) < 0)
279 node
->left
= tn
->right
;
283 if (node
->left
== NULL
)
293 if (node
->right
== NULL
)
296 if ((ret
= dict
->compare_cb(key
, node
->right
->key
)) > 0)
299 node
->right
= tn
->left
;
303 if (node
->right
== NULL
)
313 left
->right
= node
->left
;
314 right
->left
= node
->right
;
316 node
->left
= n
.right
;
317 node
->right
= n
.left
;
323 * irc_dictionary_link(struct Dictionary *dict,
324 * struct DictionaryElement *delem)
326 * Links a dictionary tree element to the dictionary.
328 * When we add new nodes to the tree, it becomes the
329 * next nominated root. This is perhaps not a wise
330 * optimization because of automatic retuning, but
331 * it keeps the code simple.
335 * - dictionary tree element
341 * - a node is linked to the dictionary tree
344 irc_dictionary_link(struct Dictionary
*dict
,
345 struct DictionaryElement
*delem
)
347 s_assert(dict
!= NULL
);
348 s_assert(delem
!= NULL
);
354 if (dict
->root
== NULL
)
356 delem
->left
= delem
->right
= NULL
;
357 delem
->next
= delem
->prev
= NULL
;
358 dict
->head
= dict
->tail
= dict
->root
= delem
;
364 irc_dictionary_retune(dict
, delem
->key
);
366 if ((ret
= dict
->compare_cb(delem
->key
, dict
->root
->key
)) < 0)
368 delem
->left
= dict
->root
->left
;
369 delem
->right
= dict
->root
;
370 dict
->root
->left
= NULL
;
372 if (dict
->root
->prev
)
373 dict
->root
->prev
->next
= delem
;
377 delem
->prev
= dict
->root
->prev
;
378 delem
->next
= dict
->root
;
379 dict
->root
->prev
= delem
;
384 delem
->right
= dict
->root
->right
;
385 delem
->left
= dict
->root
;
386 dict
->root
->right
= NULL
;
388 if (dict
->root
->next
)
389 dict
->root
->next
->prev
= delem
;
393 delem
->next
= dict
->root
->next
;
394 delem
->prev
= dict
->root
;
395 dict
->root
->next
= delem
;
400 dict
->root
->key
= delem
->key
;
401 dict
->root
->data
= delem
->data
;
404 BlockHeapFree(elem_heap
, delem
);
410 * irc_dictionary_unlink_root(struct Dictionary *dict)
412 * Unlinks the root dictionary tree element from the dictionary.
421 * - the root node is unlinked from the dictionary tree
424 irc_dictionary_unlink_root(struct Dictionary
*dict
)
426 struct DictionaryElement
*delem
, *nextnode
, *parentofnext
;
434 if (dict
->root
->left
== NULL
)
435 dict
->root
= dict
->root
->right
;
436 else if (dict
->root
->right
== NULL
)
437 dict
->root
= dict
->root
->left
;
440 /* Make the node with the next highest key the new root.
441 * This node has a NULL left pointer. */
442 nextnode
= delem
->next
;
443 s_assert(nextnode
->left
== NULL
);
444 if (nextnode
== delem
->right
)
446 dict
->root
= nextnode
;
447 dict
->root
->left
= delem
->left
;
451 parentofnext
= delem
->right
;
452 while (parentofnext
->left
!= NULL
&& parentofnext
->left
!= nextnode
)
453 parentofnext
= parentofnext
->left
;
454 s_assert(parentofnext
->left
== nextnode
);
455 parentofnext
->left
= nextnode
->right
;
456 dict
->root
= nextnode
;
457 dict
->root
->left
= delem
->left
;
458 dict
->root
->right
= delem
->right
;
463 if (delem
->prev
!= NULL
)
464 delem
->prev
->next
= delem
->next
;
466 if (dict
->head
== delem
)
467 dict
->head
= delem
->next
;
470 delem
->next
->prev
= delem
->prev
;
472 if (dict
->tail
== delem
)
473 dict
->tail
= delem
->prev
;
479 * irc_dictionary_destroy(struct Dictionary *dtree,
480 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
483 * Recursively destroys all nodes in a dictionary tree.
486 * - dictionary tree object
487 * - optional iteration callback
488 * - optional opaque/private data to pass to callback
494 * - on success, a dtree and optionally it's children are destroyed.
497 * - if this is called without a callback, the objects bound to the
498 * DTree will not be destroyed.
500 void irc_dictionary_destroy(struct Dictionary
*dtree
,
501 void (*destroy_cb
)(struct DictionaryElement
*delem
, void *privdata
),
504 struct DictionaryElement
*n
, *tn
;
506 s_assert(dtree
!= NULL
);
508 DLINK_FOREACH_SAFE(n
, tn
, dtree
->head
)
510 if (destroy_cb
!= NULL
)
511 (*destroy_cb
)(n
, privdata
);
513 BlockHeapFree(elem_heap
, n
);
520 * irc_dictionary_foreach(struct Dictionary *dtree,
521 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
524 * Iterates over all entries in a DTree.
527 * - dictionary tree object
528 * - optional iteration callback
529 * - optional opaque/private data to pass to callback
535 * - on success, a dtree is iterated
537 void irc_dictionary_foreach(struct Dictionary
*dtree
,
538 int (*foreach_cb
)(struct DictionaryElement
*delem
, void *privdata
),
541 struct DictionaryElement
*n
, *tn
;
543 s_assert(dtree
!= NULL
);
545 DLINK_FOREACH_SAFE(n
, tn
, dtree
->head
)
547 /* delem_t is a subclass of node_t. */
548 struct DictionaryElement
*delem
= (struct DictionaryElement
*) n
;
550 if (foreach_cb
!= NULL
)
551 (*foreach_cb
)(delem
, privdata
);
556 * irc_dictionary_search(struct Dictionary *dtree,
557 * void (*destroy_cb)(struct DictionaryElement *delem, void *privdata),
560 * Searches all entries in a DTree using a custom callback.
563 * - dictionary tree object
564 * - optional iteration callback
565 * - optional opaque/private data to pass to callback
568 * - on success, the requested object
569 * - on failure, NULL.
572 * - a dtree is iterated until the requested conditions are met
574 void *irc_dictionary_search(struct Dictionary
*dtree
,
575 void *(*foreach_cb
)(struct DictionaryElement
*delem
, void *privdata
),
578 struct DictionaryElement
*n
, *tn
;
581 s_assert(dtree
!= NULL
);
583 DLINK_FOREACH_SAFE(n
, tn
, dtree
->head
)
585 /* delem_t is a subclass of node_t. */
586 struct DictionaryElement
*delem
= (struct DictionaryElement
*) n
;
588 if (foreach_cb
!= NULL
)
589 ret
= (*foreach_cb
)(delem
, privdata
);
599 * irc_dictionary_foreach_start(struct Dictionary *dtree,
600 * struct DictionaryIter *state);
602 * Initializes a static DTree iterator.
605 * - dictionary tree object
606 * - static DTree iterator
612 * - the static iterator, &state, is initialized.
614 void irc_dictionary_foreach_start(struct Dictionary
*dtree
,
615 struct DictionaryIter
*state
)
617 s_assert(dtree
!= NULL
);
618 s_assert(state
!= NULL
);
623 /* find first item */
624 state
->cur
= dtree
->head
;
626 if (state
->cur
== NULL
)
629 /* make state->cur point to first item and state->next point to
631 state
->next
= state
->cur
;
632 irc_dictionary_foreach_next(dtree
, state
);
636 * irc_dictionary_foreach_cur(struct Dictionary *dtree,
637 * struct DictionaryIter *state);
639 * Returns the data from the current node being iterated by the
643 * - dictionary tree object
644 * - static DTree iterator
647 * - reference to data in the current dtree node being iterated
652 void *irc_dictionary_foreach_cur(struct Dictionary
*dtree
,
653 struct DictionaryIter
*state
)
655 s_assert(dtree
!= NULL
);
656 s_assert(state
!= NULL
);
658 return state
->cur
!= NULL
? state
->cur
->data
: NULL
;
662 * irc_dictionary_foreach_next(struct Dictionary *dtree,
663 * struct DictionaryIter *state);
665 * Advances a static DTree iterator.
668 * - dictionary tree object
669 * - static DTree iterator
675 * - the static iterator, &state, is advanced to a new DTree node.
677 void irc_dictionary_foreach_next(struct Dictionary
*dtree
,
678 struct DictionaryIter
*state
)
680 s_assert(dtree
!= NULL
);
681 s_assert(state
!= NULL
);
683 if (state
->cur
== NULL
)
685 ilog(L_MAIN
, "irc_dictionary_foreach_next(): called again after iteration finished on dtree<%p>", dtree
);
689 state
->cur
= state
->next
;
691 if (state
->next
== NULL
)
694 state
->next
= state
->next
->next
;
698 * irc_dictionary_find(struct Dictionary *dtree, const char *key)
700 * Looks up a DTree node by name.
703 * - dictionary tree object
704 * - name of node to lookup
707 * - on success, the dtree node requested
713 struct DictionaryElement
*irc_dictionary_find(struct Dictionary
*dict
, const char *key
)
715 s_assert(dict
!= NULL
);
716 s_assert(key
!= NULL
);
718 /* retune for key, key will be the tree's root if it's available */
719 irc_dictionary_retune(dict
, key
);
721 if (dict
->root
&& !dict
->compare_cb(key
, dict
->root
->key
))
728 * irc_dictionary_add(struct Dictionary *dtree, const char *key, void *data)
730 * Creates a new DTree node and binds data to it.
733 * - dictionary tree object
734 * - name for new DTree node
735 * - data to bind to the new DTree node
738 * - on success, a new DTree node
742 * - data is inserted into the DTree.
744 struct DictionaryElement
*irc_dictionary_add(struct Dictionary
*dict
, char *key
, void *data
)
746 struct DictionaryElement
*delem
;
748 s_assert(dict
!= NULL
);
749 s_assert(key
!= NULL
);
750 s_assert(data
!= NULL
);
751 s_assert(irc_dictionary_find(dict
, key
) == NULL
);
753 delem
= BlockHeapAlloc(elem_heap
);
757 /* TBD: is this needed? --nenolod */
758 if (delem
->key
== NULL
)
760 BlockHeapFree(elem_heap
, delem
);
764 irc_dictionary_link(dict
, delem
);
770 * irc_dictionary_delete(struct Dictionary *dtree, const char *key)
772 * Deletes data from a dictionary tree.
775 * - dictionary tree object
776 * - name of DTree node to delete
779 * - on success, the remaining data that needs to be mowgli_freed
783 * - data is removed from the DTree.
786 * - the returned data needs to be mowgli_freed/released manually!
788 void *irc_dictionary_delete(struct Dictionary
*dtree
, const char *key
)
790 struct DictionaryElement
*delem
= irc_dictionary_find(dtree
, key
);
798 irc_dictionary_unlink_root(dtree
);
799 BlockHeapFree(elem_heap
, delem
);
805 * irc_dictionary_retrieve(struct Dictionary *dtree, const char *key)
807 * Retrieves data from a dictionary.
810 * - dictionary tree object
811 * - name of node to lookup
814 * - on success, the data bound to the DTree node.
820 void *irc_dictionary_retrieve(struct Dictionary
*dtree
, const char *key
)
822 struct DictionaryElement
*delem
= irc_dictionary_find(dtree
, key
);
831 * irc_dictionary_size(struct Dictionary *dict)
833 * Returns the size of a dictionary.
836 * - dictionary tree object
839 * - size of dictionary
844 unsigned int irc_dictionary_size(struct Dictionary
*dict
)
846 s_assert(dict
!= NULL
);
851 /* returns the sum of the depths of the subtree rooted in delem at depth depth */
853 stats_recurse(struct DictionaryElement
*delem
, int depth
, int *pmaxdepth
)
857 if (depth
> *pmaxdepth
)
861 result
+= stats_recurse(delem
->left
, depth
+ 1, pmaxdepth
);
863 result
+= stats_recurse(delem
->right
, depth
+ 1, pmaxdepth
);
868 * irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata)
870 * Returns the size of a dictionary.
873 * - dictionary tree object
875 * - data for callback
881 * - callback called with stats text
883 void irc_dictionary_stats(struct Dictionary
*dict
, void (*cb
)(const char *line
, void *privdata
), void *privdata
)
888 s_assert(dict
!= NULL
);
890 if (dict
->id
!= NULL
)
891 snprintf(str
, sizeof str
, "Dictionary stats for %s (%d)",
892 dict
->id
, dict
->count
);
894 snprintf(str
, sizeof str
, "Dictionary stats for <%p> (%d)",
898 sum
= stats_recurse(dict
->root
, 0, &maxdepth
);
899 snprintf(str
, sizeof str
, "Depth sum %d Avg depth %d Max depth %d", sum
, sum
/ dict
->count
, maxdepth
);