/* * 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 "match.h" #include "client.h" #include "setup.h" #include "irc_dictionary.h" static rb_bh *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 *) rb_malloc(sizeof(struct Dictionary)); dtree->compare_cb = compare_cb; if (!elem_heap) elem_heap = rb_bh_create(sizeof(struct DictionaryElement), 1024, "dictionary_elem_heap"); 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 *) rb_malloc(sizeof(struct Dictionary)); dtree->compare_cb = compare_cb; dtree->id = rb_strdup(name); if (!elem_heap) elem_heap = rb_bh_create(sizeof(struct DictionaryElement), 1024, "dictionary_elem_heap"); 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. * * Inputs: * - node to begin search from * * Outputs: * - none * * Side Effects: * - a new root node is nominated. */ static 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 */ static 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--; rb_bh_free(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 */ static 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); RB_DLINK_FOREACH_SAFE(n, tn, dtree->head) { if (destroy_cb != NULL) (*destroy_cb)(n, privdata); rb_bh_free(elem_heap, n); } rb_free(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); RB_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); RB_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>", (void *)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, const 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 = rb_bh_alloc(elem_heap); delem->key = key; delem->data = data; /* TBD: is this needed? --nenolod */ if (delem->key == NULL) { rb_bh_free(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); rb_bh_free(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)", (void *)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; }