From d6bda36db1ae0e51393b89c2a04ea92083461c3c Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 2 Dec 2007 09:06:25 -0600 Subject: [PATCH] Add irc_dictionary code. --- include/irc_dictionary.h | 155 +++++++ src/Makefile.in | 1 + src/irc_dictionary.c | 902 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1058 insertions(+) create mode 100644 include/irc_dictionary.h create mode 100644 src/irc_dictionary.c diff --git a/include/irc_dictionary.h b/include/irc_dictionary.h new file mode 100644 index 000000000..0dac5456c --- /dev/null +++ b/include/irc_dictionary.h @@ -0,0 +1,155 @@ +/* + * charybdis: an advanced ircd. + * irc_dictionary.h: Dictionary-based storage. + * + * Copyright (c) 2007 William Pitcock + * Copyright (c) 2007 Jilles Tjoelker + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __IRC_DICTIONARY_H__ +#define __IRC_DICTIONARY_H__ + +struct Dictionary; /* defined in src/dictionary.c */ + +typedef int (*DCF)(const char *a, const char *b); + +struct DictionaryElement +{ + struct DictionaryElement *left, *right, *prev, *next; + void *data; + char *key; + int position; +}; + +struct DictionaryIter +{ + struct DictionaryElement *cur, *next; +}; + +/* + * this is a convenience macro for inlining iteration of dictionaries. + */ +#define DICTIONARY_FOREACH(element, state, dict) for (irc_dictionary_foreach_start((dict), (state)); (element = irc_dictionary_foreach_cur((dict), (state))); irc_dictionary_foreach_next((dict), (state))) + +/* + * irc_dictionary_create() creates a new dictionary tree. + * compare_cb is the comparison function, typically strcmp, strcasecmp or + * irccasecmp. + */ +extern struct Dictionary *irc_dictionary_create(DCF compare_cb); + +/* + * irc_dictionary_create_named() creates a new dictionary tree which has a name. + * name is the name, compare_cb is the comparator. + */ +extern struct Dictionary *irc_dictionary_create_named(const char *name, DCF compare_cb); + +/* + * irc_dictionary_set_comparator_func() resets the comparator used for lookups and + * insertions in the DTree structure. + */ +extern void irc_dictionary_set_comparator_func(struct Dictionary *dict, + DCF compare_cb); + +/* + * irc_dictionary_get_comparator_func() returns the comparator used for lookups and + * insertions in the DTree structure. + */ +extern DCF irc_dictionary_get_comparator_func(struct Dictionary *dict); + +/* + * irc_dictionary_get_linear_index() returns the linear index of an object in the + * DTree structure. + */ +extern int irc_dictionary_get_linear_index(struct Dictionary *dict, const char *key); + +/* + * irc_dictionary_destroy() destroys all entries in a dtree, and also optionally calls + * a defined callback function to destroy any data attached to it. + */ +extern void irc_dictionary_destroy(struct Dictionary *dtree, + void (*destroy_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata); + +/* + * irc_dictionary_foreach() iterates all entries in a dtree, and also optionally calls + * a defined callback function to use any data attached to it. + * + * To shortcircuit iteration, return non-zero from the callback function. + */ +extern void irc_dictionary_foreach(struct Dictionary *dtree, + int (*foreach_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata); + +/* + * irc_dictionary_search() iterates all entries in a dtree, and also optionally calls + * a defined callback function to use any data attached to it. + * + * When the object is found, a non-NULL is returned from the callback, which results + * in that object being returned to the user. + */ +extern void *irc_dictionary_search(struct Dictionary *dtree, + void *(*foreach_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata); + +/* + * irc_dictionary_foreach_start() begins an iteration over all items + * keeping state in the given struct. If there is only one iteration + * in progress at a time, it is permitted to remove the current element + * of the iteration (but not any other element). + */ +extern void irc_dictionary_foreach_start(struct Dictionary *dtree, + struct DictionaryIter *state); + +/* + * irc_dictionary_foreach_cur() returns the current element of the iteration, + * or NULL if there are no more elements. + */ +extern void *irc_dictionary_foreach_cur(struct Dictionary *dtree, + struct DictionaryIter *state); + +/* + * irc_dictionary_foreach_next() moves to the next element. + */ +extern void irc_dictionary_foreach_next(struct Dictionary *dtree, + struct DictionaryIter *state); + +/* + * irc_dictionary_add() adds a key->value entry to the dictionary tree. + */ +extern struct DictionaryElement *irc_dictionary_add(struct Dictionary *dtree, char *key, void *data); + +/* + * irc_dictionary_find() returns a struct DictionaryElement container from a dtree for key 'key'. + */ +extern struct DictionaryElement *irc_dictionary_find(struct Dictionary *dtree, const char *key); + +/* + * irc_dictionary_find() returns data from a dtree for key 'key'. + */ +extern void *irc_dictionary_retrieve(struct Dictionary *dtree, const char *key); + +/* + * irc_dictionary_delete() deletes a key->value entry from the dictionary tree. + */ +extern void *irc_dictionary_delete(struct Dictionary *dtree, const char *key); + +void irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata); + +#endif diff --git a/src/Makefile.in b/src/Makefile.in index 2d8c470ec..a9d906710 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -66,6 +66,7 @@ SRCS = \ hook.c \ hostmask.c \ irc_string.c \ + irc_dictionary.c \ ircd.c \ ircd_signal.c \ ircd_state.c \ diff --git a/src/irc_dictionary.c b/src/irc_dictionary.c new file mode 100644 index 000000000..b393e54b1 --- /dev/null +++ b/src/irc_dictionary.c @@ -0,0 +1,902 @@ +/* + * charybdis: an advanced ircd + * irc_dictionary.c: Dictionary-based information storage. + * + * Copyright (c) 2007 William Pitcock + * Copyright (c) 2007 Jilles Tjoelker + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "sprintf_irc.h" +#include "tools.h" +#include "irc_string.h" +#include "client.h" +#include "memory.h" +#include "setup.h" +#include "balloc.h" +#include "irc_dictionary.h" + +static BlockHeap *elem_heap = NULL; + +struct Dictionary +{ + DCF compare_cb; + struct DictionaryElement *root, *head, *tail; + unsigned int count; + char *id; + unsigned int dirty:1; +}; + +/* + * irc_dictionary_create(DCF compare_cb) + * + * Dictionary object factory. + * + * Inputs: + * - function to use for comparing two entries in the dtree + * + * Outputs: + * - on success, a new dictionary object. + * + * Side Effects: + * - if services runs out of memory and cannot allocate the object, + * the program will abort. + */ +struct Dictionary *irc_dictionary_create(DCF compare_cb) +{ + struct Dictionary *dtree = (struct Dictionary *) MyMalloc(sizeof(struct Dictionary)); + + dtree->compare_cb = compare_cb; + + if (!elem_heap) + elem_heap = BlockHeapCreate(sizeof(struct DictionaryElement), 1024); + + return dtree; +} + +/* + * irc_dictionary_create_named(const char *name, + * DCF compare_cb) + * + * Dictionary object factory. + * + * Inputs: + * - dictionary name + * - function to use for comparing two entries in the dtree + * + * Outputs: + * - on success, a new dictionary object. + * + * Side Effects: + * - if services runs out of memory and cannot allocate the object, + * the program will abort. + */ +struct Dictionary *irc_dictionary_create_named(const char *name, + DCF compare_cb) +{ + struct Dictionary *dtree = (struct Dictionary *) MyMalloc(sizeof(struct Dictionary)); + + dtree->compare_cb = compare_cb; + DupString(dtree->id, name); + + if (!elem_heap) + elem_heap = BlockHeapCreate(sizeof(struct DictionaryElement), 1024); + + return dtree; +} + +/* + * irc_dictionary_set_comparator_func(struct Dictionary *dict, + * DCF compare_cb) + * + * Resets the comparator function used by the dictionary code for + * updating the DTree structure. + * + * Inputs: + * - dictionary object + * - new comparator function (passed as functor) + * + * Outputs: + * - nothing + * + * Side Effects: + * - the dictionary comparator function is reset. + */ +void irc_dictionary_set_comparator_func(struct Dictionary *dict, + DCF compare_cb) +{ + s_assert(dict != NULL); + s_assert(compare_cb != NULL); + + dict->compare_cb = compare_cb; +} + +/* + * irc_dictionary_get_comparator_func(struct Dictionary *dict) + * + * Returns the current comparator function used by the dictionary. + * + * Inputs: + * - dictionary object + * + * Outputs: + * - comparator function (returned as functor) + * + * Side Effects: + * - none + */ +DCF +irc_dictionary_get_comparator_func(struct Dictionary *dict) +{ + s_assert(dict != NULL); + + return dict->compare_cb; +} + +/* + * irc_dictionary_get_linear_index(struct Dictionary *dict, + * const char *key) + * + * Gets a linear index number for key. + * + * Inputs: + * - dictionary tree object + * - pointer to data + * + * Outputs: + * - position, from zero. + * + * Side Effects: + * - rebuilds the linear index if the tree is marked as dirty. + */ +int +irc_dictionary_get_linear_index(struct Dictionary *dict, const char *key) +{ + struct DictionaryElement *elem; + + s_assert(dict != NULL); + s_assert(key != NULL); + + elem = irc_dictionary_find(dict, key); + if (elem == NULL) + return -1; + + if (!dict->dirty) + return elem->position; + else + { + struct DictionaryElement *delem; + int i; + + for (delem = dict->head, i = 0; delem != NULL; delem = delem->next, i++) + delem->position = i; + + dict->dirty = FALSE; + } + + return elem->position; +} + +/* + * irc_dictionary_retune(struct Dictionary *dict, const char *key) + * + * Retunes the tree, self-optimizing for the element which belongs to key. + * + * Tuning the tree structure is a very complex operation. Unlike + * 2-3-4 trees and BTree/BTree+ structures, this structure is a + * constantly evolving algorithm. + * + * Instead of maintaining a balanced tree, we constantly adapt the + * tree by nominating a new root nearby the most recently looked up + * or added data. We are constantly retuning ourselves instead of + * doing massive O(n) rebalance operations as seen in BTrees, + * and the level of data stored in a tree is dynamic, instead of being + * held to a restricted design like other trees. + * + * Moreover, we are different than a radix/patricia tree, because we + * don't statically allocate positions. Radix trees have the advantage + * of not requiring tuning or balancing operations while having the + * disadvantage of requiring a large amount of memory to store + * large trees. Our efficiency as far as speed goes is not as + * fast as a radix tree; but is close to it. + * + * The retuning algorithm uses the comparison callback that is + * passed in the initialization of the tree container. If the + * comparator returns a value which is less than zero, we push the + * losing node out of the way, causing it to later be reparented + * with another node. The winning child of this comparison is always + * the right-most node. + * + * Once we have reached the key which has been targeted, or have reached + * a deadend, we nominate the nearest node as the new root of the tree. + * If an exact match has been found, the new root becomes the node which + * represents key. + * + * This results in a tree which can self-optimize for both critical + * conditions: nodes which are distant and similar and trees which + * have ordered lookups. + * + * Inputs: + * - node to begin search from + * + * Outputs: + * - none + * + * Side Effects: + * - a new root node is nominated. + */ +void +irc_dictionary_retune(struct Dictionary *dict, const char *key) +{ + struct DictionaryElement n, *tn, *left, *right, *node; + int ret; + + s_assert(dict != NULL); + + if (dict->root == NULL) + return; + + /* + * we initialize n with known values, since it's on stack + * memory. otherwise the dict would become corrupted. + * + * n is used for temporary storage while the tree is retuned. + * -nenolod + */ + n.left = n.right = NULL; + left = right = &n; + + /* this for(;;) loop is the main workhorse of the rebalancing */ + for (node = dict->root; ; ) + { + if ((ret = dict->compare_cb(key, node->key)) == 0) + break; + + if (ret < 0) + { + if (node->left == NULL) + break; + + if ((ret = dict->compare_cb(key, node->left->key)) < 0) + { + tn = node->left; + node->left = tn->right; + tn->right = node; + node = tn; + + if (node->left == NULL) + break; + } + + right->left = node; + right = node; + node = node->left; + } + else + { + if (node->right == NULL) + break; + + if ((ret = dict->compare_cb(key, node->right->key)) > 0) + { + tn = node->right; + node->right = tn->left; + tn->left = node; + node = tn; + + if (node->right == NULL) + break; + } + + left->right = node; + left = node; + node = node->right; + } + } + + left->right = node->left; + right->left = node->right; + + node->left = n.right; + node->right = n.left; + + dict->root = node; +} + +/* + * irc_dictionary_link(struct Dictionary *dict, + * struct DictionaryElement *delem) + * + * Links a dictionary tree element to the dictionary. + * + * When we add new nodes to the tree, it becomes the + * next nominated root. This is perhaps not a wise + * optimization because of automatic retuning, but + * it keeps the code simple. + * + * Inputs: + * - dictionary tree + * - dictionary tree element + * + * Outputs: + * - nothing + * + * Side Effects: + * - a node is linked to the dictionary tree + */ +void +irc_dictionary_link(struct Dictionary *dict, + struct DictionaryElement *delem) +{ + s_assert(dict != NULL); + s_assert(delem != NULL); + + dict->dirty = TRUE; + + dict->count++; + + if (dict->root == NULL) + { + delem->left = delem->right = NULL; + delem->next = delem->prev = NULL; + dict->head = dict->tail = dict->root = delem; + } + else + { + int ret; + + irc_dictionary_retune(dict, delem->key); + + if ((ret = dict->compare_cb(delem->key, dict->root->key)) < 0) + { + delem->left = dict->root->left; + delem->right = dict->root; + dict->root->left = NULL; + + if (dict->root->prev) + dict->root->prev->next = delem; + else + dict->head = delem; + + delem->prev = dict->root->prev; + delem->next = dict->root; + dict->root->prev = delem; + dict->root = delem; + } + else if (ret > 0) + { + delem->right = dict->root->right; + delem->left = dict->root; + dict->root->right = NULL; + + if (dict->root->next) + dict->root->next->prev = delem; + else + dict->tail = delem; + + delem->next = dict->root->next; + delem->prev = dict->root; + dict->root->next = delem; + dict->root = delem; + } + else + { + dict->root->key = delem->key; + dict->root->data = delem->data; + dict->count--; + + BlockHeapFree(elem_heap, delem); + } + } +} + +/* + * irc_dictionary_unlink_root(struct Dictionary *dict) + * + * Unlinks the root dictionary tree element from the dictionary. + * + * Inputs: + * - dictionary tree + * + * Outputs: + * - nothing + * + * Side Effects: + * - the root node is unlinked from the dictionary tree + */ +void +irc_dictionary_unlink_root(struct Dictionary *dict) +{ + struct DictionaryElement *delem, *nextnode, *parentofnext; + + dict->dirty = TRUE; + + delem = dict->root; + if (delem == NULL) + return; + + if (dict->root->left == NULL) + dict->root = dict->root->right; + else if (dict->root->right == NULL) + dict->root = dict->root->left; + else + { + /* Make the node with the next highest key the new root. + * This node has a NULL left pointer. */ + nextnode = delem->next; + s_assert(nextnode->left == NULL); + if (nextnode == delem->right) + { + dict->root = nextnode; + dict->root->left = delem->left; + } + else + { + parentofnext = delem->right; + while (parentofnext->left != NULL && parentofnext->left != nextnode) + parentofnext = parentofnext->left; + s_assert(parentofnext->left == nextnode); + parentofnext->left = nextnode->right; + dict->root = nextnode; + dict->root->left = delem->left; + dict->root->right = delem->right; + } + } + + /* linked list */ + if (delem->prev != NULL) + delem->prev->next = delem->next; + + if (dict->head == delem) + dict->head = delem->next; + + if (delem->next) + delem->next->prev = delem->prev; + + if (dict->tail == delem) + dict->tail = delem->prev; + + dict->count--; +} + +/* + * irc_dictionary_destroy(struct Dictionary *dtree, + * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata), + * void *privdata); + * + * Recursively destroys all nodes in a dictionary tree. + * + * Inputs: + * - dictionary tree object + * - optional iteration callback + * - optional opaque/private data to pass to callback + * + * Outputs: + * - nothing + * + * Side Effects: + * - on success, a dtree and optionally it's children are destroyed. + * + * Notes: + * - if this is called without a callback, the objects bound to the + * DTree will not be destroyed. + */ +void irc_dictionary_destroy(struct Dictionary *dtree, + void (*destroy_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata) +{ + struct DictionaryElement *n, *tn; + + s_assert(dtree != NULL); + + DLINK_FOREACH_SAFE(n, tn, dtree->head) + { + if (destroy_cb != NULL) + (*destroy_cb)(n, privdata); + + BlockHeapFree(elem_heap, n); + } + + MyFree(dtree); +} + +/* + * irc_dictionary_foreach(struct Dictionary *dtree, + * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata), + * void *privdata); + * + * Iterates over all entries in a DTree. + * + * Inputs: + * - dictionary tree object + * - optional iteration callback + * - optional opaque/private data to pass to callback + * + * Outputs: + * - nothing + * + * Side Effects: + * - on success, a dtree is iterated + */ +void irc_dictionary_foreach(struct Dictionary *dtree, + int (*foreach_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata) +{ + struct DictionaryElement *n, *tn; + + s_assert(dtree != NULL); + + DLINK_FOREACH_SAFE(n, tn, dtree->head) + { + /* delem_t is a subclass of node_t. */ + struct DictionaryElement *delem = (struct DictionaryElement *) n; + + if (foreach_cb != NULL) + (*foreach_cb)(delem, privdata); + } +} + +/* + * irc_dictionary_search(struct Dictionary *dtree, + * void (*destroy_cb)(struct DictionaryElement *delem, void *privdata), + * void *privdata); + * + * Searches all entries in a DTree using a custom callback. + * + * Inputs: + * - dictionary tree object + * - optional iteration callback + * - optional opaque/private data to pass to callback + * + * Outputs: + * - on success, the requested object + * - on failure, NULL. + * + * Side Effects: + * - a dtree is iterated until the requested conditions are met + */ +void *irc_dictionary_search(struct Dictionary *dtree, + void *(*foreach_cb)(struct DictionaryElement *delem, void *privdata), + void *privdata) +{ + struct DictionaryElement *n, *tn; + void *ret = NULL; + + s_assert(dtree != NULL); + + DLINK_FOREACH_SAFE(n, tn, dtree->head) + { + /* delem_t is a subclass of node_t. */ + struct DictionaryElement *delem = (struct DictionaryElement *) n; + + if (foreach_cb != NULL) + ret = (*foreach_cb)(delem, privdata); + + if (ret) + break; + } + + return ret; +} + +/* + * irc_dictionary_foreach_start(struct Dictionary *dtree, + * struct DictionaryIter *state); + * + * Initializes a static DTree iterator. + * + * Inputs: + * - dictionary tree object + * - static DTree iterator + * + * Outputs: + * - nothing + * + * Side Effects: + * - the static iterator, &state, is initialized. + */ +void irc_dictionary_foreach_start(struct Dictionary *dtree, + struct DictionaryIter *state) +{ + s_assert(dtree != NULL); + s_assert(state != NULL); + + state->cur = NULL; + state->next = NULL; + + /* find first item */ + state->cur = dtree->head; + + if (state->cur == NULL) + return; + + /* make state->cur point to first item and state->next point to + * second item */ + state->next = state->cur; + irc_dictionary_foreach_next(dtree, state); +} + +/* + * irc_dictionary_foreach_cur(struct Dictionary *dtree, + * struct DictionaryIter *state); + * + * Returns the data from the current node being iterated by the + * static iterator. + * + * Inputs: + * - dictionary tree object + * - static DTree iterator + * + * Outputs: + * - reference to data in the current dtree node being iterated + * + * Side Effects: + * - none + */ +void *irc_dictionary_foreach_cur(struct Dictionary *dtree, + struct DictionaryIter *state) +{ + s_assert(dtree != NULL); + s_assert(state != NULL); + + return state->cur != NULL ? state->cur->data : NULL; +} + +/* + * irc_dictionary_foreach_next(struct Dictionary *dtree, + * struct DictionaryIter *state); + * + * Advances a static DTree iterator. + * + * Inputs: + * - dictionary tree object + * - static DTree iterator + * + * Outputs: + * - nothing + * + * Side Effects: + * - the static iterator, &state, is advanced to a new DTree node. + */ +void irc_dictionary_foreach_next(struct Dictionary *dtree, + struct DictionaryIter *state) +{ + s_assert(dtree != NULL); + s_assert(state != NULL); + + if (state->cur == NULL) + { + ilog(L_MAIN, "irc_dictionary_foreach_next(): called again after iteration finished on dtree<%p>", dtree); + return; + } + + state->cur = state->next; + + if (state->next == NULL) + return; + + state->next = state->next->next; +} + +/* + * irc_dictionary_find(struct Dictionary *dtree, const char *key) + * + * Looks up a DTree node by name. + * + * Inputs: + * - dictionary tree object + * - name of node to lookup + * + * Outputs: + * - on success, the dtree node requested + * - on failure, NULL + * + * Side Effects: + * - none + */ +struct DictionaryElement *irc_dictionary_find(struct Dictionary *dict, const char *key) +{ + s_assert(dict != NULL); + s_assert(key != NULL); + + /* retune for key, key will be the tree's root if it's available */ + irc_dictionary_retune(dict, key); + + if (dict->root && !dict->compare_cb(key, dict->root->key)) + return dict->root; + + return NULL; +} + +/* + * irc_dictionary_add(struct Dictionary *dtree, const char *key, void *data) + * + * Creates a new DTree node and binds data to it. + * + * Inputs: + * - dictionary tree object + * - name for new DTree node + * - data to bind to the new DTree node + * + * Outputs: + * - on success, a new DTree node + * - on failure, NULL + * + * Side Effects: + * - data is inserted into the DTree. + */ +struct DictionaryElement *irc_dictionary_add(struct Dictionary *dict, char *key, void *data) +{ + struct DictionaryElement *delem; + + s_assert(dict != NULL); + s_assert(key != NULL); + s_assert(data != NULL); + s_assert(irc_dictionary_find(dict, key) == NULL); + + delem = BlockHeapAlloc(elem_heap); + delem->key = key; + delem->data = data; + + /* TBD: is this needed? --nenolod */ + if (delem->key == NULL) + { + BlockHeapFree(elem_heap, delem); + return NULL; + } + + irc_dictionary_link(dict, delem); + + return delem; +} + +/* + * irc_dictionary_delete(struct Dictionary *dtree, const char *key) + * + * Deletes data from a dictionary tree. + * + * Inputs: + * - dictionary tree object + * - name of DTree node to delete + * + * Outputs: + * - on success, the remaining data that needs to be mowgli_freed + * - on failure, NULL + * + * Side Effects: + * - data is removed from the DTree. + * + * Notes: + * - the returned data needs to be mowgli_freed/released manually! + */ +void *irc_dictionary_delete(struct Dictionary *dtree, const char *key) +{ + struct DictionaryElement *delem = irc_dictionary_find(dtree, key); + void *data; + + if (delem == NULL) + return NULL; + + data = delem->data; + + irc_dictionary_unlink_root(dtree); + BlockHeapFree(elem_heap, delem); + + return data; +} + +/* + * irc_dictionary_retrieve(struct Dictionary *dtree, const char *key) + * + * Retrieves data from a dictionary. + * + * Inputs: + * - dictionary tree object + * - name of node to lookup + * + * Outputs: + * - on success, the data bound to the DTree node. + * - on failure, NULL + * + * Side Effects: + * - none + */ +void *irc_dictionary_retrieve(struct Dictionary *dtree, const char *key) +{ + struct DictionaryElement *delem = irc_dictionary_find(dtree, key); + + if (delem != NULL) + return delem->data; + + return NULL; +} + +/* + * irc_dictionary_size(struct Dictionary *dict) + * + * Returns the size of a dictionary. + * + * Inputs: + * - dictionary tree object + * + * Outputs: + * - size of dictionary + * + * Side Effects: + * - none + */ +unsigned int irc_dictionary_size(struct Dictionary *dict) +{ + s_assert(dict != NULL); + + return dict->count; +} + +/* returns the sum of the depths of the subtree rooted in delem at depth depth */ +static int +stats_recurse(struct DictionaryElement *delem, int depth, int *pmaxdepth) +{ + int result; + + if (depth > *pmaxdepth) + *pmaxdepth = depth; + result = depth; + if (delem->left) + result += stats_recurse(delem->left, depth + 1, pmaxdepth); + if (delem->right) + result += stats_recurse(delem->right, depth + 1, pmaxdepth); + return result; +} + +/* + * irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata) + * + * Returns the size of a dictionary. + * + * Inputs: + * - dictionary tree object + * - callback + * - data for callback + * + * Outputs: + * - none + * + * Side Effects: + * - callback called with stats text + */ +void irc_dictionary_stats(struct Dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata) +{ + char str[256]; + int sum, maxdepth; + + s_assert(dict != NULL); + + if (dict->id != NULL) + snprintf(str, sizeof str, "Dictionary stats for %s (%d)", + dict->id, dict->count); + else + snprintf(str, sizeof str, "Dictionary stats for <%p> (%d)", + dict, dict->count); + cb(str, privdata); + maxdepth = 0; + sum = stats_recurse(dict->root, 0, &maxdepth); + snprintf(str, sizeof str, "Depth sum %d Avg depth %d Max depth %d", sum, sum / dict->count, maxdepth); + cb(str, privdata); + return; +}