Add irc_dictionary code.
This commit is contained in:
parent
66c8fdd207
commit
d6bda36db1
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* charybdis: an advanced ircd.
|
||||||
|
* irc_dictionary.h: Dictionary-based storage.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
|
||||||
|
* Copyright (c) 2007 Jilles Tjoelker <jilles -at- stack.nl>
|
||||||
|
*
|
||||||
|
* 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
|
|
@ -66,6 +66,7 @@ SRCS = \
|
||||||
hook.c \
|
hook.c \
|
||||||
hostmask.c \
|
hostmask.c \
|
||||||
irc_string.c \
|
irc_string.c \
|
||||||
|
irc_dictionary.c \
|
||||||
ircd.c \
|
ircd.c \
|
||||||
ircd_signal.c \
|
ircd_signal.c \
|
||||||
ircd_state.c \
|
ircd_state.c \
|
||||||
|
|
|
@ -0,0 +1,902 @@
|
||||||
|
/*
|
||||||
|
* charybdis: an advanced ircd
|
||||||
|
* irc_dictionary.c: Dictionary-based information storage.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
|
||||||
|
* Copyright (c) 2007 Jilles Tjoelker <jilles -at- stack.nl>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
Loading…
Reference in New Issue