summaryrefslogtreecommitdiff
path: root/src/libmowgli/container
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmowgli/container')
-rw-r--r--src/libmowgli/container/Makefile23
-rw-r--r--src/libmowgli/container/dictionary.c899
-rw-r--r--src/libmowgli/container/dictionary.h166
-rw-r--r--src/libmowgli/container/index.c187
-rw-r--r--src/libmowgli/container/index.h51
-rw-r--r--src/libmowgli/container/list.c390
-rw-r--r--src/libmowgli/container/list.h76
-rw-r--r--src/libmowgli/container/patricia.c1037
-rw-r--r--src/libmowgli/container/patricia.h155
-rw-r--r--src/libmowgli/container/queue.c231
-rw-r--r--src/libmowgli/container/queue.h44
11 files changed, 3259 insertions, 0 deletions
diff --git a/src/libmowgli/container/Makefile b/src/libmowgli/container/Makefile
new file mode 100644
index 0000000..1c97ee9
--- /dev/null
+++ b/src/libmowgli/container/Makefile
@@ -0,0 +1,23 @@
+include ../../../extra.mk
+
+STATIC_PIC_LIB_NOINST = ${LIBMOWGLI_SHARED_CONTAINER}
+STATIC_LIB_NOINST = ${LIBMOWGLI_STATIC_CONTAINER}
+
+SRCS = dictionary.c \
+ list.c \
+ queue.c \
+ index.c \
+ patricia.c
+
+INCLUDES = dictionary.h \
+ list.h \
+ queue.h \
+ index.h \
+ patricia.h
+
+include ../../../buildsys.mk
+
+includesubdir = $(PACKAGE_NAME)/container
+
+CPPFLAGS += -I. -I.. -I../../.. -DMOWGLI_CORE
+
diff --git a/src/libmowgli/container/dictionary.c b/src/libmowgli/container/dictionary.c
new file mode 100644
index 0000000..22445d7
--- /dev/null
+++ b/src/libmowgli/container/dictionary.c
@@ -0,0 +1,899 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * 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 "mowgli.h"
+
+static mowgli_heap_t *elem_heap = NULL;
+
+struct mowgli_dictionary_
+{
+ mowgli_dictionary_comparator_func_t compare_cb;
+ mowgli_dictionary_elem_t *root, *head, *tail;
+ unsigned int count;
+ char *id;
+ bool dirty;
+};
+
+/*
+ * mowgli_dictionary_create(mowgli_dictionary_comparator_func_t 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.
+ */
+mowgli_dictionary_t *mowgli_dictionary_create(mowgli_dictionary_comparator_func_t compare_cb)
+{
+ mowgli_dictionary_t *dtree = (mowgli_dictionary_t *) mowgli_alloc(sizeof(mowgli_dictionary_t));
+
+ dtree->compare_cb = compare_cb;
+
+ if (!elem_heap)
+ elem_heap = mowgli_heap_create(sizeof(mowgli_dictionary_elem_t), 1024, BH_NOW);
+
+ return dtree;
+}
+
+/*
+ * mowgli_dictionary_create_named(const char *name,
+ * mowgli_dictionary_comparator_func_t 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.
+ */
+mowgli_dictionary_t *mowgli_dictionary_create_named(const char *name,
+ mowgli_dictionary_comparator_func_t compare_cb)
+{
+ mowgli_dictionary_t *dtree = (mowgli_dictionary_t *) mowgli_alloc(sizeof(mowgli_dictionary_t));
+
+ dtree->compare_cb = compare_cb;
+ dtree->id = strdup(name);
+
+ if (!elem_heap)
+ elem_heap = mowgli_heap_create(sizeof(mowgli_dictionary_elem_t), 1024, BH_NOW);
+
+ return dtree;
+}
+
+/*
+ * mowgli_dictionary_set_comparator_func(mowgli_dictionary_t *dict,
+ * mowgli_dictionary_comparator_func_t 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 mowgli_dictionary_set_comparator_func(mowgli_dictionary_t *dict,
+ mowgli_dictionary_comparator_func_t compare_cb)
+{
+ return_if_fail(dict != NULL);
+ return_if_fail(compare_cb != NULL);
+
+ dict->compare_cb = compare_cb;
+}
+
+/*
+ * mowgli_dictionary_get_comparator_func(mowgli_dictionary_t *dict)
+ *
+ * Returns the current comparator function used by the dictionary.
+ *
+ * Inputs:
+ * - dictionary object
+ *
+ * Outputs:
+ * - comparator function (returned as functor)
+ *
+ * Side Effects:
+ * - none
+ */
+mowgli_dictionary_comparator_func_t
+mowgli_dictionary_get_comparator_func(mowgli_dictionary_t *dict)
+{
+ return_val_if_fail(dict != NULL, NULL);
+
+ return dict->compare_cb;
+}
+
+/*
+ * mowgli_dictionary_get_linear_index(mowgli_dictionary_t *dict,
+ * const void *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
+mowgli_dictionary_get_linear_index(mowgli_dictionary_t *dict, const void *key)
+{
+ mowgli_dictionary_elem_t *elem;
+
+ return_val_if_fail(dict != NULL, 0);
+ return_val_if_fail(key != NULL, 0);
+
+ elem = mowgli_dictionary_find(dict, key);
+ if (elem == NULL)
+ return -1;
+
+ if (!dict->dirty)
+ return elem->position;
+ else
+ {
+ mowgli_dictionary_elem_t *delem;
+ int i;
+
+ for (delem = dict->head, i = 0; delem != NULL; delem = delem->next, i++)
+ delem->position = i;
+
+ dict->dirty = false;
+ }
+
+ return elem->position;
+}
+
+/*
+ * mowgli_dictionary_retune(mowgli_dictionary_t *dict, const void *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
+mowgli_dictionary_retune(mowgli_dictionary_t *dict, const void *key)
+{
+ mowgli_dictionary_elem_t n, *tn, *left, *right, *node;
+ ptrdiff_t ret;
+
+ return_if_fail(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;
+}
+
+/*
+ * mowgli_dictionary_link(mowgli_dictionary_t *dict,
+ * mowgli_dictionary_elem_t *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
+mowgli_dictionary_link(mowgli_dictionary_t *dict,
+ mowgli_dictionary_elem_t *delem)
+{
+ return_if_fail(dict != NULL);
+ return_if_fail(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;
+
+ mowgli_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--;
+
+ mowgli_heap_free(elem_heap, delem);
+ }
+ }
+}
+
+/*
+ * mowgli_dictionary_unlink_root(mowgli_dictionary_t *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
+mowgli_dictionary_unlink_root(mowgli_dictionary_t *dict)
+{
+ mowgli_dictionary_elem_t *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;
+ soft_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;
+ soft_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--;
+}
+
+/*
+ * mowgli_dictionary_destroy(mowgli_dictionary_t *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 mowgli_dictionary_destroy(mowgli_dictionary_t *dtree,
+ void (*destroy_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata)
+{
+ mowgli_dictionary_elem_t *n, *tn;
+
+ return_if_fail(dtree != NULL);
+
+ MOWGLI_LIST_FOREACH_SAFE(n, tn, dtree->head)
+ {
+ if (destroy_cb != NULL)
+ (*destroy_cb)(n, privdata);
+
+ mowgli_heap_free(elem_heap, n);
+ }
+
+ mowgli_free(dtree);
+}
+
+/*
+ * mowgli_dictionary_foreach(mowgli_dictionary_t *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 mowgli_dictionary_foreach(mowgli_dictionary_t *dtree,
+ int (*foreach_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata)
+{
+ mowgli_dictionary_elem_t *n, *tn;
+
+ return_if_fail(dtree != NULL);
+
+ MOWGLI_LIST_FOREACH_SAFE(n, tn, dtree->head)
+ {
+ /* delem_t is a subclass of node_t. */
+ mowgli_dictionary_elem_t *delem = (mowgli_dictionary_elem_t *) n;
+
+ if (foreach_cb != NULL)
+ (*foreach_cb)(delem, privdata);
+ }
+}
+
+/*
+ * mowgli_dictionary_search(mowgli_dictionary_t *dtree,
+ * void (*destroy_cb)(mowgli_dictionary_elem_t *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 *mowgli_dictionary_search(mowgli_dictionary_t *dtree,
+ void *(*foreach_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata)
+{
+ mowgli_dictionary_elem_t *n, *tn;
+ void *ret = NULL;
+
+ return_val_if_fail(dtree != NULL, NULL);
+
+ MOWGLI_LIST_FOREACH_SAFE(n, tn, dtree->head)
+ {
+ /* delem_t is a subclass of node_t. */
+ mowgli_dictionary_elem_t *delem = (mowgli_dictionary_elem_t *) n;
+
+ if (foreach_cb != NULL)
+ ret = (*foreach_cb)(delem, privdata);
+
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * mowgli_dictionary_foreach_start(mowgli_dictionary_t *dtree,
+ * mowgli_dictionary_iteration_state_t *state);
+ *
+ * Initializes a static DTree iterator.
+ *
+ * Inputs:
+ * - dictionary tree object
+ * - static DTree iterator
+ *
+ * Outputs:
+ * - nothing
+ *
+ * Side Effects:
+ * - the static iterator, &state, is initialized.
+ */
+void mowgli_dictionary_foreach_start(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state)
+{
+ return_if_fail(dtree != NULL);
+ return_if_fail(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;
+ mowgli_dictionary_foreach_next(dtree, state);
+}
+
+/*
+ * mowgli_dictionary_foreach_cur(mowgli_dictionary_t *dtree,
+ * mowgli_dictionary_iteration_state_t *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 *mowgli_dictionary_foreach_cur(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state)
+{
+ return_val_if_fail(dtree != NULL, NULL);
+ return_val_if_fail(state != NULL, NULL);
+
+ return state->cur != NULL ? state->cur->data : NULL;
+}
+
+/*
+ * mowgli_dictionary_foreach_next(mowgli_dictionary_t *dtree,
+ * mowgli_dictionary_iteration_state_t *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 mowgli_dictionary_foreach_next(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state)
+{
+ return_if_fail(dtree != NULL);
+ return_if_fail(state != NULL);
+
+ if (state->cur == NULL)
+ {
+ mowgli_log("mowgli_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;
+}
+
+/*
+ * mowgli_dictionary_find(mowgli_dictionary_t *dtree, const void *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
+ */
+mowgli_dictionary_elem_t *mowgli_dictionary_find(mowgli_dictionary_t *dict, const void *key)
+{
+ return_val_if_fail(dict != NULL, NULL);
+ return_val_if_fail(key != NULL, NULL);
+
+ /* retune for key, key will be the tree's root if it's available */
+ mowgli_dictionary_retune(dict, key);
+
+ if (dict->root && !dict->compare_cb(key, dict->root->key))
+ return dict->root;
+
+ return NULL;
+}
+
+/*
+ * mowgli_dictionary_add(mowgli_dictionary_t *dtree, const void *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.
+ */
+mowgli_dictionary_elem_t *mowgli_dictionary_add(mowgli_dictionary_t *dict, const void *key, void *data)
+{
+ mowgli_dictionary_elem_t *delem;
+
+ return_val_if_fail(dict != NULL, NULL);
+ return_val_if_fail(key != NULL, NULL);
+ return_val_if_fail(data != NULL, NULL);
+ return_val_if_fail(mowgli_dictionary_find(dict, key) == NULL, NULL);
+
+ delem = mowgli_heap_alloc(elem_heap);
+ delem->key = key;
+ delem->data = data;
+
+ if (delem->key == NULL)
+ {
+ mowgli_log("major WTF: delem->key is NULL, not adding node.", key);
+ mowgli_heap_free(elem_heap, delem);
+ return NULL;
+ }
+
+ mowgli_dictionary_link(dict, delem);
+
+ return delem;
+}
+
+/*
+ * mowgli_dictionary_delete(mowgli_dictionary_t *dtree, const void *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 *mowgli_dictionary_delete(mowgli_dictionary_t *dtree, const void *key)
+{
+ mowgli_dictionary_elem_t *delem = mowgli_dictionary_find(dtree, key);
+ void *data;
+
+ if (delem == NULL)
+ return NULL;
+
+ data = delem->data;
+
+ mowgli_dictionary_unlink_root(dtree);
+ mowgli_heap_free(elem_heap, delem);
+
+ return data;
+}
+
+/*
+ * mowgli_dictionary_retrieve(mowgli_dictionary_t *dtree, const void *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 *mowgli_dictionary_retrieve(mowgli_dictionary_t *dtree, const void *key)
+{
+ mowgli_dictionary_elem_t *delem = mowgli_dictionary_find(dtree, key);
+
+ if (delem != NULL)
+ return delem->data;
+
+ return NULL;
+}
+
+/*
+ * mowgli_dictionary_size(mowgli_dictionary_t *dict)
+ *
+ * Returns the size of a dictionary.
+ *
+ * Inputs:
+ * - dictionary tree object
+ *
+ * Outputs:
+ * - size of dictionary
+ *
+ * Side Effects:
+ * - none
+ */
+unsigned int mowgli_dictionary_size(mowgli_dictionary_t *dict)
+{
+ return_val_if_fail(dict != NULL, 0);
+
+ return dict->count;
+}
+
+/* returns the sum of the depths of the subtree rooted in delem at depth depth */
+static int
+stats_recurse(mowgli_dictionary_elem_t *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;
+}
+
+/*
+ * mowgli_dictionary_stats(mowgli_dictionary_t *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 mowgli_dictionary_stats(mowgli_dictionary_t *dict, void (*cb)(const char *line, void *privdata), void *privdata)
+{
+ char str[256];
+ int sum, maxdepth;
+
+ return_if_fail(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;
+ if (dict->root != NULL)
+ {
+ 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);
+ }
+ else
+ snprintf(str, sizeof str, "Depth sum 0 Avg depth 0 Max depth 0");
+ cb(str, privdata);
+ return;
+}
diff --git a/src/libmowgli/container/dictionary.h b/src/libmowgli/container/dictionary.h
new file mode 100644
index 0000000..3c2f4e5
--- /dev/null
+++ b/src/libmowgli/container/dictionary.h
@@ -0,0 +1,166 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * 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 __MOWGLI_DICTIONARY_H__
+#define __MOWGLI_DICTIONARY_H__
+
+struct mowgli_dictionary_; /* defined in src/dictionary.c */
+
+typedef struct mowgli_dictionary_ mowgli_dictionary_t;
+
+typedef struct mowgli_dictionary_elem_ mowgli_dictionary_elem_t;
+
+typedef ptrdiff_t (*mowgli_dictionary_comparator_func_t)(const void *a, const void *b);
+
+struct mowgli_dictionary_elem_
+{
+ mowgli_dictionary_elem_t *left, *right, *prev, *next;
+ void *data;
+ const void *key;
+ int position;
+};
+
+/*
+ * mowgli_dictionary_iteration_state_t, private.
+ */
+struct mowgli_dictionary_iteration_state_
+{
+ mowgli_dictionary_elem_t *cur, *next;
+ void *pspare[4];
+ int ispare[4];
+};
+
+typedef struct mowgli_dictionary_iteration_state_ mowgli_dictionary_iteration_state_t;
+
+/*
+ * this is a convenience macro for inlining iteration of dictionaries.
+ */
+#define MOWGLI_DICTIONARY_FOREACH(element, state, dict) for (mowgli_dictionary_foreach_start((dict), (state)); (element = mowgli_dictionary_foreach_cur((dict), (state))); mowgli_dictionary_foreach_next((dict), (state)))
+
+/*
+ * mowgli_dictionary_create() creates a new dictionary tree.
+ * compare_cb is the comparison function, typically strcmp, strcasecmp or
+ * irccasecmp.
+ */
+extern mowgli_dictionary_t *mowgli_dictionary_create(mowgli_dictionary_comparator_func_t compare_cb);
+
+/*
+ * mowgli_dictionary_create_named() creates a new dictionary tree which has a name.
+ * name is the name, compare_cb is the comparator.
+ */
+extern mowgli_dictionary_t *mowgli_dictionary_create_named(const char *name, mowgli_dictionary_comparator_func_t compare_cb);
+
+/*
+ * mowgli_dictionary_set_comparator_func() resets the comparator used for lookups and
+ * insertions in the DTree structure.
+ */
+extern void mowgli_dictionary_set_comparator_func(mowgli_dictionary_t *dict,
+ mowgli_dictionary_comparator_func_t compare_cb);
+
+/*
+ * mowgli_dictionary_get_comparator_func() returns the comparator used for lookups and
+ * insertions in the DTree structure.
+ */
+extern mowgli_dictionary_comparator_func_t mowgli_dictionary_get_comparator_func(mowgli_dictionary_t *dict);
+
+/*
+ * mowgli_dictionary_get_linear_index() returns the linear index of an object in the
+ * DTree structure.
+ */
+extern int mowgli_dictionary_get_linear_index(mowgli_dictionary_t *dict, const void *key);
+
+/*
+ * mowgli_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 mowgli_dictionary_destroy(mowgli_dictionary_t *dtree,
+ void (*destroy_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_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 mowgli_dictionary_foreach(mowgli_dictionary_t *dtree,
+ int (*foreach_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_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 *mowgli_dictionary_search(mowgli_dictionary_t *dtree,
+ void *(*foreach_cb)(mowgli_dictionary_elem_t *delem, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_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 mowgli_dictionary_foreach_start(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state);
+
+/*
+ * mowgli_dictionary_foreach_cur() returns the current element of the iteration,
+ * or NULL if there are no more elements.
+ */
+extern void *mowgli_dictionary_foreach_cur(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state);
+
+/*
+ * mowgli_dictionary_foreach_next() moves to the next element.
+ */
+extern void mowgli_dictionary_foreach_next(mowgli_dictionary_t *dtree,
+ mowgli_dictionary_iteration_state_t *state);
+
+/*
+ * mowgli_dictionary_add() adds a key->value entry to the dictionary tree.
+ */
+extern mowgli_dictionary_elem_t *mowgli_dictionary_add(mowgli_dictionary_t *dtree, const void *key, void *data);
+
+/*
+ * mowgli_dictionary_find() returns a mowgli_dictionary_elem_t container from a dtree for key 'key'.
+ */
+extern mowgli_dictionary_elem_t *mowgli_dictionary_find(mowgli_dictionary_t *dtree, const void *key);
+
+/*
+ * mowgli_dictionary_find() returns data from a dtree for key 'key'.
+ */
+extern void *mowgli_dictionary_retrieve(mowgli_dictionary_t *dtree, const void *key);
+
+/*
+ * mowgli_dictionary_delete() deletes a key->value entry from the dictionary tree.
+ */
+extern void *mowgli_dictionary_delete(mowgli_dictionary_t *dtree, const void *key);
+
+void mowgli_dictionary_stats(mowgli_dictionary_t *dict, void (*cb)(const char *line, void *privdata), void *privdata);
+
+#endif
diff --git a/src/libmowgli/container/index.c b/src/libmowgli/container/index.c
new file mode 100644
index 0000000..81776f5
--- /dev/null
+++ b/src/libmowgli/container/index.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2009-2011 John Lindgren
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include "mowgli.h"
+
+struct mowgli_index_
+{
+ void * * data;
+ int count, size;
+ int (* compare) (const void * a, const void * b, void * data);
+ void * compare_data;
+};
+
+static mowgli_heap_t *index_heap = NULL;
+
+void mowgli_index_init (void)
+{
+ index_heap = mowgli_heap_create(sizeof(mowgli_index_t), 32, BH_NOW);
+}
+
+mowgli_index_t * mowgli_index_create (void)
+{
+ mowgli_index_t * index = mowgli_heap_alloc(index_heap);
+
+ index->data = NULL;
+ index->count = 0;
+ index->size = 0;
+ index->compare = NULL;
+ index->compare_data = NULL;
+
+ return index;
+}
+
+void mowgli_index_destroy (mowgli_index_t * index)
+{
+ mowgli_free (index->data);
+ mowgli_heap_free (index_heap, index);
+}
+
+int mowgli_index_count (mowgli_index_t * index)
+{
+ return index->count;
+}
+
+void mowgli_index_allocate (mowgli_index_t * index, int size)
+{
+ size_t oldsize;
+ void *new_ptr;
+
+ if (size <= index->size)
+ return;
+
+ if (! index->size)
+ index->size = 64;
+
+ oldsize = index->size;
+ while (size > index->size)
+ index->size <<= 1;
+
+ new_ptr = mowgli_alloc_array(sizeof (void *), index->size);
+
+ if (index->data != NULL)
+ {
+ memcpy(new_ptr, index->data, oldsize);
+ mowgli_free(index->data);
+ }
+
+ index->data = new_ptr;
+}
+
+void mowgli_index_set (mowgli_index_t * index, int at, void * value)
+{
+ index->data[at] = value;
+}
+
+void * mowgli_index_get (mowgli_index_t * index, int at)
+{
+ return index->data[at];
+}
+
+static void make_room (mowgli_index_t * index, int at, int count)
+{
+ mowgli_index_allocate (index, index->count + count);
+
+ if (at < index->count)
+ memmove (index->data + at + count, index->data + at, sizeof (void *) *
+ (index->count - at));
+
+ index->count += count;
+}
+
+void mowgli_index_insert (mowgli_index_t * index, int at, void * value)
+{
+ make_room (index, at, 1);
+ index->data[at] = value;
+}
+
+void mowgli_index_append (mowgli_index_t * index, void * value)
+{
+ mowgli_index_insert (index, index->count, value);
+}
+
+void mowgli_index_copy_set (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int to, int count)
+{
+ memcpy (target->data + to, source->data + from, sizeof (void *) * count);
+}
+
+void mowgli_index_copy_insert (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int to, int count)
+{
+ make_room (target, to, count);
+ memcpy (target->data + to, source->data + from, sizeof (void *) * count);
+}
+
+void mowgli_index_copy_append (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int count)
+{
+ mowgli_index_copy_insert (source, from, target, target->count, count);
+}
+
+void mowgli_index_merge_insert (mowgli_index_t * first, int at, mowgli_index_t * second)
+{
+ mowgli_index_copy_insert (second, 0, first, at, second->count);
+}
+
+void mowgli_index_merge_append (mowgli_index_t * first, mowgli_index_t * second)
+{
+ mowgli_index_copy_insert (second, 0, first, first->count, second->count);
+}
+
+void mowgli_index_move (mowgli_index_t * index, int from, int to, int count)
+{
+ memmove (index->data + to, index->data + from, sizeof (void *) * count);
+}
+
+void mowgli_index_delete (mowgli_index_t * index, int at, int count)
+{
+ index->count -= count;
+ memmove (index->data + at, index->data + at + count, sizeof (void *) *
+ (index->count - at));
+}
+
+void mowgli_index_sort (mowgli_index_t * index, int (* compare) (const void *, const void *))
+{
+ qsort(index->data, index->count, sizeof (void *), compare);
+}
+
+#ifdef NOTYET
+static int mowgli_index_compare_with_data (const void * a, const void * b, void * _index)
+{
+ mowgli_index_t * index = _index;
+
+ return index->compare (* (const void * *) a, * (const void * *) b,
+ index->compare_data);
+}
+
+void mowgli_index_sort_with_data (mowgli_index_t * index, int (* compare)
+ (const void * a, const void * b, void * data), void * data)
+{
+ index->compare = compare;
+ index->compare_data = data;
+ g_qsort_with_data (index->data, index->count, sizeof (void *),
+ mowgli_index_compare_with_data, index);
+ index->compare = NULL;
+ index->compare_data = NULL;
+}
+#endif
diff --git a/src/libmowgli/container/index.h b/src/libmowgli/container/index.h
new file mode 100644
index 0000000..85e9e72
--- /dev/null
+++ b/src/libmowgli/container/index.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2009-2011 John Lindgren
+ *
+ * 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 __MOWGLI_INDEX_H__
+#define __MOWGLI_INDEX_H__
+
+struct mowgli_index_;
+
+typedef struct mowgli_index_ mowgli_index_t;
+
+mowgli_index_t * mowgli_index_create (void);
+void mowgli_index_destroy (mowgli_index_t * index);
+int mowgli_index_count (mowgli_index_t * index);
+void mowgli_index_allocate (mowgli_index_t * index, int size);
+void mowgli_index_set (mowgli_index_t * index, int at, void * value);
+void * mowgli_index_get (mowgli_index_t * index, int at);
+void mowgli_index_insert (mowgli_index_t * index, int at, void * value);
+void mowgli_index_append (mowgli_index_t * index, void * value);
+void mowgli_index_copy_set (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int to, int count);
+void mowgli_index_copy_insert (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int to, int count);
+void mowgli_index_copy_append (mowgli_index_t * source, int from, mowgli_index_t * target,
+ int count);
+void mowgli_index_merge_insert (mowgli_index_t * first, int at, mowgli_index_t * second);
+void mowgli_index_merge_append (mowgli_index_t * first, mowgli_index_t * second);
+void mowgli_index_move (mowgli_index_t * index, int from, int to, int count);
+void mowgli_index_delete (mowgli_index_t * index, int at, int count);
+void mowgli_index_sort (mowgli_index_t * index, int (* compare) (const void * a,
+ const void * b));
+void mowgli_index_sort_with_data (mowgli_index_t * index, int (* compare)
+ (const void * a, const void * b, void * data), void * data);
+
+#endif
diff --git a/src/libmowgli/container/list.c b/src/libmowgli/container/list.c
new file mode 100644
index 0000000..6283910
--- /dev/null
+++ b/src/libmowgli/container/list.c
@@ -0,0 +1,390 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * list.c: Linked lists.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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 "mowgli.h"
+
+static mowgli_heap_t *mowgli_node_heap;
+static mowgli_heap_t *mowgli_list_heap;
+
+void mowgli_node_bootstrap(void)
+{
+ mowgli_node_heap = mowgli_heap_create(sizeof(mowgli_node_t), 1024, BH_NOW);
+ mowgli_list_heap = mowgli_heap_create(sizeof(mowgli_list_t), 64, BH_NOW);
+
+ if (mowgli_node_heap == NULL || mowgli_list_heap == NULL)
+ {
+ mowgli_log("heap allocator failure.");
+ abort();
+ }
+}
+
+/* creates a new node */
+mowgli_node_t *mowgli_node_create(void)
+{
+ mowgli_node_t *n;
+
+ /* allocate it */
+ n = mowgli_heap_alloc(mowgli_node_heap);
+
+ /* initialize */
+ n->next = n->prev = n->data = NULL;
+
+ /* return a pointer to the new node */
+ return n;
+}
+
+/* frees a node */
+void mowgli_node_free(mowgli_node_t *n)
+{
+ return_if_fail(n != NULL);
+
+ /* free it */
+ mowgli_heap_free(mowgli_node_heap, n);
+}
+
+/* adds a node to the end of a list */
+void mowgli_node_add(void *data, mowgli_node_t *n, mowgli_list_t *l)
+{
+ mowgli_node_t *tn;
+
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ n->next = n->prev = NULL;
+ n->data = data;
+
+ /* first node? */
+ if (l->head == NULL)
+ {
+ l->head = n;
+ l->tail = n;
+ l->count++;
+ return;
+ }
+
+ /* use the cached tail. */
+ tn = l->tail;
+
+ /* set the our `prev' to the last node */
+ n->prev = tn;
+
+ /* set the last node's `next' to us */
+ n->prev->next = n;
+
+ /* set the list's `tail' to us */
+ l->tail = n;
+
+ /* up the count */
+ l->count++;
+}
+
+/* adds a node to the head of a list */
+void mowgli_node_add_head(void *data, mowgli_node_t *n, mowgli_list_t *l)
+{
+ mowgli_node_t *tn;
+
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ n->next = n->prev = NULL;
+ n->data = data;
+
+ /* first node? */
+ if (!l->head)
+ {
+ l->head = n;
+ l->tail = n;
+ l->count++;
+ return;
+ }
+
+ tn = l->head;
+ n->next = tn;
+ tn->prev = n;
+ l->head = n;
+ l->count++;
+}
+
+/* adds a node to a list before another node, or to the end */
+void mowgli_node_add_before(void *data, mowgli_node_t *n, mowgli_list_t *l, mowgli_node_t *before)
+{
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ if (before == NULL)
+ mowgli_node_add(data, n, l);
+ else if (before == l->head)
+ mowgli_node_add_head(data, n, l);
+ else
+ {
+ n->data = data;
+ n->prev = before->prev;
+ n->next = before;
+ before->prev = n;
+
+ if (n->prev != NULL)
+ n->prev->next = n;
+
+ l->count++;
+ }
+}
+
+/* adds a node to a list after another node, or to the end */
+void mowgli_node_add_after(void *data, mowgli_node_t *n, mowgli_list_t *l, mowgli_node_t *before)
+{
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ if (before == NULL || before->next == NULL)
+ mowgli_node_add(data, n, l);
+ else
+ {
+ n->data = data;
+ n->prev = before;
+ n->next = before->next;
+ before->next = n;
+ n->next->prev = n;
+ l->count++;
+ }
+}
+
+/* retrieves a node at `position` position. */
+mowgli_node_t *mowgli_node_nth(mowgli_list_t *l, size_t pos)
+{
+ size_t iter;
+ mowgli_node_t *n;
+
+ return_val_if_fail(l != NULL, NULL);
+
+ /* locate the proper position. */
+ if (pos < MOWGLI_LIST_LENGTH(l) / 2)
+ for (iter = 0, n = l->head; iter != pos && n != NULL; iter++, n = n->next);
+ else
+ for (iter = MOWGLI_LIST_LENGTH(l) - 1, n = l->tail;
+ iter != pos && n != NULL; iter--, n = n->prev);
+
+ return n;
+}
+
+/* returns the data from node at `position` position, or NULL. */
+void *mowgli_node_nth_data(mowgli_list_t *l, size_t pos)
+{
+ mowgli_node_t *n;
+
+ return_val_if_fail(l != NULL, NULL);
+
+ n = mowgli_node_nth(l, pos);
+
+ if (n == NULL)
+ return NULL;
+
+ return n->data;
+}
+
+/* inserts a node at `position` position. */
+void mowgli_node_insert(void *data, mowgli_node_t *n, mowgli_list_t *l, size_t pos)
+{
+ mowgli_node_t *tn;
+
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ /* locate the proper position. */
+ tn = mowgli_node_nth(l, pos);
+
+ mowgli_node_add_before(data, n, l, tn);
+}
+
+/* retrieves the index position of a node in a list. */
+ssize_t mowgli_node_index(mowgli_node_t *n, mowgli_list_t *l)
+{
+ ssize_t iter;
+ mowgli_node_t *tn;
+
+ return_val_if_fail(n != NULL, -1);
+ return_val_if_fail(l != NULL, -1);
+
+ /* locate the proper position. */
+ for (iter = 0, tn = l->head; tn != n && tn != NULL; iter++, tn = tn->next);
+
+ return iter < (ssize_t) MOWGLI_LIST_LENGTH(l) ? iter : -1;
+}
+
+/* deletes a link between a node and a list. */
+void mowgli_node_delete(mowgli_node_t *n, mowgli_list_t *l)
+{
+ return_if_fail(n != NULL);
+ return_if_fail(l != NULL);
+
+ /* are we the head? */
+ if (!n->prev)
+ l->head = n->next;
+ else
+ n->prev->next = n->next;
+
+ /* are we the tail? */
+ if (!n->next)
+ l->tail = n->prev;
+ else
+ n->next->prev = n->prev;
+
+ /* down the count */
+ l->count--;
+}
+
+/* finds a node by `data' */
+mowgli_node_t *mowgli_node_find(void *data, mowgli_list_t *l)
+{
+ mowgli_node_t *n;
+
+ return_val_if_fail(l != NULL, NULL);
+
+ MOWGLI_LIST_FOREACH(n, l->head) if (n->data == data)
+ return n;
+
+ return NULL;
+}
+
+/* moves a node from one list to another. */
+void mowgli_node_move(mowgli_node_t *m, mowgli_list_t *oldlist, mowgli_list_t *newlist)
+{
+ return_if_fail(m != NULL);
+ return_if_fail(oldlist != NULL);
+ return_if_fail(newlist != NULL);
+
+ /* Assumption: If m->next == NULL, then list->tail == m
+ * and: If m->prev == NULL, then list->head == m
+ */
+ if (m->next != NULL)
+ m->next->prev = m->prev;
+ else
+ oldlist->tail = m->prev;
+
+ if (m->prev != NULL)
+ m->prev->next = m->next;
+ else
+ oldlist->head = m->next;
+
+ m->prev = NULL;
+ m->next = newlist->head;
+
+ if (newlist->head != NULL)
+ newlist->head->prev = m;
+ else if (newlist->tail == NULL)
+ newlist->tail = m;
+
+ newlist->head = m;
+
+ oldlist->count--;
+ newlist->count++;
+}
+
+/* creates a new list. */
+mowgli_list_t *mowgli_list_create(void)
+{
+ mowgli_list_t *out = mowgli_heap_alloc(mowgli_list_heap);
+
+ return out;
+}
+
+/* frees a created list. */
+void mowgli_list_free(mowgli_list_t *l)
+{
+ mowgli_heap_free(mowgli_list_heap, l);
+}
+
+/* concatenates two lists together. */
+void mowgli_list_concat(mowgli_list_t *l, mowgli_list_t *l2)
+{
+ return_if_fail(l != NULL);
+ return_if_fail(l2 != NULL);
+
+ if (l->tail != NULL)
+ l->tail->next = l2->head;
+
+ if (l->tail->next != NULL)
+ l->tail->next->prev = l->tail;
+
+ l->tail = l2->tail;
+ l->count += l2->count;
+
+ /* clear out l2 as it is no longer needed. */
+ l2->head = l2->tail = NULL;
+ l2->count = 0;
+}
+
+/* reverse a list -- O(n)! */
+void mowgli_list_reverse(mowgli_list_t *l)
+{
+ mowgli_node_t *n, *tn;
+
+ return_if_fail(l != NULL);
+
+ MOWGLI_LIST_FOREACH_SAFE(n, tn, l->head)
+ {
+ mowgli_node_t *tn2 = n->next;
+ n->next = n->prev;
+ n->prev = tn2;
+ }
+
+ tn = l->head;
+ l->head = l->tail;
+ l->tail = tn;
+}
+
+/* sorts a list -- O(n ^ 2) most likely, i don't want to think about it. --nenolod */
+void mowgli_list_sort(mowgli_list_t *l, mowgli_list_comparator_t comp, void *opaque)
+{
+ mowgli_node_t *n, *tn, *n2, *tn2;
+
+ return_if_fail(l != NULL);
+ return_if_fail(comp != NULL);
+
+ MOWGLI_LIST_FOREACH_SAFE(n, tn, l->head)
+ {
+ MOWGLI_LIST_FOREACH_SAFE(n2, tn2, l->head)
+ {
+ int result;
+ int i, i2;
+
+ if (n == n2)
+ continue;
+
+ i = mowgli_node_index(n, l);
+ i2 = mowgli_node_index(n2, l);
+
+ if ((result = comp(n, n2, opaque)) == 0)
+ continue;
+ else if (result < 0 && i > i2)
+ {
+ mowgli_node_delete(n, l);
+ mowgli_node_add_before(n->data, n, l, n2);
+ }
+ else if (result > 0 && i < i2)
+ {
+ mowgli_node_delete(n, l);
+ mowgli_node_add_after(n->data, n, l, n2);
+ }
+ }
+ }
+}
diff --git a/src/libmowgli/container/list.h b/src/libmowgli/container/list.h
new file mode 100644
index 0000000..19905b4
--- /dev/null
+++ b/src/libmowgli/container/list.h
@@ -0,0 +1,76 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * list.h: Linked lists.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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 __MOWGLI_LIST_H__
+#define __MOWGLI_LIST_H__
+
+/* macros for linked lists */
+#define MOWGLI_LIST_FOREACH(n, head) for (n = (head); n; n = n->next)
+#define MOWGLI_LIST_FOREACH_NEXT(n, head) for (n = (head); n->next; n = n->next)
+#define MOWGLI_LIST_FOREACH_PREV(n, tail) for (n = (tail); n; n = n->prev)
+
+#define MOWGLI_LIST_LENGTH(list) (list)->count
+
+#define MOWGLI_LIST_FOREACH_SAFE(n, tn, head) for (n = (head), tn = n ? n->next : NULL; n != NULL; n = tn, tn = n ? n->next : NULL)
+
+/* list node struct */
+typedef struct mowgli_node_ mowgli_node_t;
+typedef struct mowgli_list_ mowgli_list_t;
+
+struct mowgli_node_
+{
+ struct mowgli_node_ *next, *prev;
+ void *data; /* pointer to real structure */
+};
+
+/* node list struct */
+struct mowgli_list_
+{
+ mowgli_node_t *head, *tail;
+ size_t count; /* how many entries in the list */
+};
+
+extern void mowgli_node_bootstrap(void);
+extern mowgli_node_t *mowgli_node_create(void);
+extern void mowgli_node_free(mowgli_node_t *n);
+extern void mowgli_node_add(void *data, mowgli_node_t *n, mowgli_list_t *l);
+extern void mowgli_node_add_head(void *data, mowgli_node_t *n, mowgli_list_t *l);
+extern void mowgli_node_add_before(void *data, mowgli_node_t *n, mowgli_list_t *l, mowgli_node_t *before);
+extern void mowgli_node_add_after(void *data, mowgli_node_t *n, mowgli_list_t *l, mowgli_node_t *before);
+extern void mowgli_node_insert(void *data, mowgli_node_t *n, mowgli_list_t *l, size_t position);
+extern ssize_t mowgli_node_index(mowgli_node_t *n, mowgli_list_t *l);
+extern void mowgli_node_delete(mowgli_node_t *n, mowgli_list_t *l);
+extern mowgli_node_t *mowgli_node_find(void *data, mowgli_list_t *l);
+extern void mowgli_node_move(mowgli_node_t *m, mowgli_list_t *oldlist, mowgli_list_t *newlist);
+extern mowgli_node_t *mowgli_node_nth(mowgli_list_t *l, size_t pos);
+extern void *mowgli_node_nth_data(mowgli_list_t *l, size_t pos);
+
+typedef int (*mowgli_list_comparator_t)(mowgli_node_t *n, mowgli_node_t *n2, void *opaque);
+
+extern mowgli_list_t *mowgli_list_create(void);
+extern void mowgli_list_free(mowgli_list_t *l);
+extern void mowgli_list_concat(mowgli_list_t *l, mowgli_list_t *l2);
+extern void mowgli_list_reverse(mowgli_list_t *l);
+extern void mowgli_list_sort(mowgli_list_t *l, mowgli_list_comparator_t comp, void *opaque);
+
+#endif
diff --git a/src/libmowgli/container/patricia.c b/src/libmowgli/container/patricia.c
new file mode 100644
index 0000000..b530d33
--- /dev/null
+++ b/src/libmowgli/container/patricia.c
@@ -0,0 +1,1037 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * patricia.c: Dictionary-based information storage.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ * Copyright (c) 2007-2010 Jilles Tjoelker <jilles -at- stack.nl>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 "mowgli.h"
+
+static mowgli_heap_t *leaf_heap = NULL;
+static mowgli_heap_t *node_heap = NULL;
+
+/*
+ * Patricia tree.
+ *
+ * A radix trie that avoids one-way branching and redundant nodes.
+ *
+ * To find a node, the tree is traversed starting from the root. The
+ * nibnum in each node indicates which nibble of the key needs to be
+ * tested, and the appropriate branch is taken.
+ *
+ * The nibnum values are strictly increasing while going down the tree.
+ *
+ * -- jilles
+ */
+
+union patricia_elem;
+
+struct mowgli_patricia_
+{
+ void (*canonize_cb)(char *key);
+ union patricia_elem *root;
+ unsigned int count;
+ char *id;
+};
+
+#define POINTERS_PER_NODE 16
+#define NIBBLE_VAL(key, nibnum) (((key)[(nibnum) / 2] >> ((nibnum) & 1 ? 0 : 4)) & 0xF)
+
+struct patricia_node
+{
+ /* nibble to test (nibble NUM%2 of byte NUM/2) */
+ int nibnum;
+ /* branches of the tree */
+ union patricia_elem *down[POINTERS_PER_NODE];
+ union patricia_elem *parent;
+ char parent_val;
+};
+
+/* mowgli_patricia_elem_ is the name used in mowgli_patricia.h.
+ * patricia_leaf is the name historically used here. */
+#define patricia_leaf mowgli_patricia_elem_
+
+struct patricia_leaf
+{
+ /* -1 to indicate this is a leaf, not a node */
+ int nibnum;
+ /* data associated with the key */
+ void *data;
+ /* key (canonized copy) */
+ char *key;
+ union patricia_elem *parent;
+ char parent_val;
+};
+
+union patricia_elem
+{
+ int nibnum;
+ struct patricia_node node;
+ struct patricia_leaf leaf;
+};
+
+#define IS_LEAF(elem) ((elem)->nibnum == -1)
+
+/* Preserve compatibility with the old mowgli_patricia.h */
+#define STATE_CUR(state) ((state)->pspare[0])
+#define STATE_NEXT(state) ((state)->pspare[1])
+
+/*
+ * first_leaf()
+ *
+ * Find the smallest leaf hanging off a subtree.
+ *
+ * Inputs:
+ * - element (may be leaf or node) heading subtree
+ *
+ * Outputs:
+ * - lowest leaf in subtree
+ *
+ * Side Effects:
+ * - none
+ */
+static union patricia_elem *first_leaf(union patricia_elem *delem)
+{
+ int val;
+
+ while (!IS_LEAF(delem))
+ {
+ for (val = 0; val < POINTERS_PER_NODE; val++)
+ {
+ if (delem->node.down[val] != NULL)
+ {
+ delem = delem->node.down[val];
+ break;
+ }
+ }
+ }
+ return delem;
+}
+
+/*
+ * mowgli_patricia_create(void (*canonize_cb)(char *key))
+ *
+ * Dictionary object factory.
+ *
+ * Inputs:
+ * - function to use for canonizing keys (for example, use
+ * a function that makes the string upper case to create
+ * a patricia with case-insensitive matching)
+ *
+ * Outputs:
+ * - on success, a new patricia object.
+ *
+ * Side Effects:
+ * - if services runs out of memory and cannot allocate the object,
+ * the program will abort.
+ */
+mowgli_patricia_t *mowgli_patricia_create(void (*canonize_cb)(char *key))
+{
+ mowgli_patricia_t *dtree = (mowgli_patricia_t *) mowgli_alloc(sizeof(mowgli_patricia_t));
+
+ dtree->canonize_cb = canonize_cb;
+
+ if (!leaf_heap)
+ leaf_heap = mowgli_heap_create(sizeof(struct patricia_leaf), 1024, BH_NOW);
+ if (!node_heap)
+ node_heap = mowgli_heap_create(sizeof(struct patricia_node), 128, BH_NOW);
+
+ dtree->root = NULL;
+
+ return dtree;
+}
+
+/*
+ * mowgli_patricia_create_named(const char *name,
+ * void (*canonize_cb)(char *key))
+ *
+ * Dictionary object factory.
+ *
+ * Inputs:
+ * - patricia name
+ * - function to use for canonizing keys (for example, use
+ * a function that makes the string upper case to create
+ * a patricia with case-insensitive matching)
+ *
+ * Outputs:
+ * - on success, a new patricia object.
+ *
+ * Side Effects:
+ * - if services runs out of memory and cannot allocate the object,
+ * the program will abort.
+ */
+mowgli_patricia_t *mowgli_patricia_create_named(const char *name,
+ void (*canonize_cb)(char *key))
+{
+ mowgli_patricia_t *dtree = (mowgli_patricia_t *) mowgli_alloc(sizeof(mowgli_patricia_t));
+
+ dtree->canonize_cb = canonize_cb;
+ dtree->id = mowgli_strdup(name);
+
+ if (!leaf_heap)
+ leaf_heap = mowgli_heap_create(sizeof(struct patricia_leaf), 1024, BH_NOW);
+ if (!node_heap)
+ node_heap = mowgli_heap_create(sizeof(struct patricia_node), 128, BH_NOW);
+
+ dtree->root = NULL;
+
+ return dtree;
+}
+
+/*
+ * mowgli_patricia_shutdown(void)
+ *
+ * Clean up after patricia to ensure all memory is released as soon as
+ * possible (destroys both heaps).
+ *
+ * Inputs:
+ * - nothing
+ *
+ * Outputs:
+ * - nothing
+ *
+ * Side Effects:
+ * - patricia's internal heaps are destroyed and deallocated
+ */
+void mowgli_patricia_shutdown(void)
+{
+ if(leaf_heap)
+ mowgli_heap_destroy(leaf_heap);
+ if(node_heap)
+ mowgli_heap_destroy(node_heap);
+
+ return;
+}
+
+/*
+ * mowgli_patricia_destroy(mowgli_patricia_t *dtree,
+ * void (*destroy_cb)(const char *key, void *data, void *privdata),
+ * void *privdata);
+ *
+ * Recursively destroys all nodes in a patricia tree.
+ *
+ * Inputs:
+ * - patricia 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 mowgli_patricia_destroy(mowgli_patricia_t *dtree,
+ void (*destroy_cb)(const char *key, void *data, void *privdata),
+ void *privdata)
+{
+ mowgli_patricia_iteration_state_t state;
+ union patricia_elem *delem;
+ void *entry;
+
+ return_if_fail(dtree != NULL);
+
+ MOWGLI_PATRICIA_FOREACH(entry, &state, dtree)
+ {
+ delem = STATE_CUR(&state);
+ if (destroy_cb != NULL)
+ (*destroy_cb)(delem->leaf.key, delem->leaf.data,
+ privdata);
+ mowgli_patricia_delete(dtree, delem->leaf.key);
+ }
+
+ mowgli_free(dtree);
+}
+
+/*
+ * mowgli_patricia_foreach(mowgli_patricia_t *dtree,
+ * int (*foreach_cb)(const char *key, void *data, void *privdata),
+ * void *privdata);
+ *
+ * Iterates over all entries in a DTree.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - optional iteration callback
+ * - optional opaque/private data to pass to callback
+ *
+ * Outputs:
+ * - nothing
+ *
+ * Side Effects:
+ * - on success, a dtree is iterated
+ */
+void mowgli_patricia_foreach(mowgli_patricia_t *dtree,
+ int (*foreach_cb)(const char *key, void *data, void *privdata),
+ void *privdata)
+{
+ union patricia_elem *delem, *next;
+ int val;
+
+ return_if_fail(dtree != NULL);
+
+ delem = dtree->root;
+ if (delem == NULL)
+ return;
+ /* Only one element in the tree */
+ if (IS_LEAF(delem))
+ {
+ if (foreach_cb != NULL)
+ (*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);
+ return;
+ }
+ val = 0;
+ do
+ {
+ do
+ next = delem->node.down[val++];
+ while (next == NULL && val < POINTERS_PER_NODE);
+ if (next != NULL)
+ {
+ if (IS_LEAF(next))
+ {
+ if (foreach_cb != NULL)
+ (*foreach_cb)(next->leaf.key, next->leaf.data, privdata);
+ }
+ else
+ {
+ delem = next;
+ val = 0;
+ }
+ }
+ while (val >= POINTERS_PER_NODE)
+ {
+ val = delem->node.parent_val;
+ delem = delem->node.parent;
+ if (delem == NULL)
+ break;
+ val++;
+ }
+ } while (delem != NULL);
+}
+
+/*
+ * mowgli_patricia_search(mowgli_patricia_t *dtree,
+ * void *(*foreach_cb)(const char *key, void *data, void *privdata),
+ * void *privdata);
+ *
+ * Searches all entries in a DTree using a custom callback.
+ *
+ * Inputs:
+ * - patricia 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 *mowgli_patricia_search(mowgli_patricia_t *dtree,
+ void *(*foreach_cb)(const char *key, void *data, void *privdata),
+ void *privdata)
+{
+ union patricia_elem *delem, *next;
+ int val;
+ void *ret = NULL;
+
+ return_val_if_fail(dtree != NULL, NULL);
+
+ delem = dtree->root;
+ if (delem == NULL)
+ return NULL;
+ /* Only one element in the tree */
+ if (IS_LEAF(delem))
+ {
+ if (foreach_cb != NULL)
+ return (*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);
+ return NULL;
+ }
+ val = 0;
+ for (;;)
+ {
+ do
+ next = delem->node.down[val++];
+ while (next == NULL && val < POINTERS_PER_NODE);
+ if (next != NULL)
+ {
+ if (IS_LEAF(next))
+ {
+ if (foreach_cb != NULL)
+ ret = (*foreach_cb)(next->leaf.key, next->leaf.data, privdata);
+ if (ret != NULL)
+ break;
+ }
+ else
+ {
+ delem = next;
+ val = 0;
+ }
+ }
+ while (val >= POINTERS_PER_NODE)
+ {
+ val = delem->node.parent_val;
+ delem = delem->node.parent;
+ if (delem == NULL)
+ break;
+ val++;
+ }
+ }
+ return ret;
+}
+
+/*
+ * mowgli_patricia_foreach_start(mowgli_patricia_t *dtree,
+ * mowgli_patricia_iteration_state_t *state);
+ *
+ * Initializes a static DTree iterator.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - static DTree iterator
+ *
+ * Outputs:
+ * - nothing
+ *
+ * Side Effects:
+ * - the static iterator, &state, is initialized.
+ */
+void mowgli_patricia_foreach_start(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state)
+{
+ if (dtree == NULL)
+ return;
+
+ return_if_fail(state != NULL);
+
+ if (dtree->root != NULL)
+ STATE_NEXT(state) = first_leaf(dtree->root);
+ else
+ STATE_NEXT(state) = NULL;
+ STATE_CUR(state) = STATE_NEXT(state);
+
+ if (STATE_NEXT(state) == NULL)
+ return;
+
+ /* make STATE_CUR point to first item and STATE_NEXT point to
+ * second item */
+ mowgli_patricia_foreach_next(dtree, state);
+}
+
+/*
+ * mowgli_patricia_foreach_cur(mowgli_patricia_t *dtree,
+ * mowgli_patricia_iteration_state_t *state);
+ *
+ * Returns the data from the current node being iterated by the
+ * static iterator.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - static DTree iterator
+ *
+ * Outputs:
+ * - reference to data in the current dtree node being iterated
+ *
+ * Side Effects:
+ * - none
+ */
+void *mowgli_patricia_foreach_cur(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state)
+{
+ if (dtree == NULL)
+ return NULL;
+
+ return_val_if_fail(state != NULL, NULL);
+
+ return STATE_CUR(state) != NULL ?
+ ((struct patricia_leaf *)STATE_CUR(state))->data : NULL;
+}
+
+/*
+ * mowgli_patricia_foreach_next(mowgli_patricia_t *dtree,
+ * mowgli_patricia_iteration_state_t *state);
+ *
+ * Advances a static DTree iterator.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - static DTree iterator
+ *
+ * Outputs:
+ * - nothing
+ *
+ * Side Effects:
+ * - the static iterator, &state, is advanced to a new DTree node.
+ */
+void mowgli_patricia_foreach_next(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state)
+{
+ struct patricia_leaf *leaf;
+ union patricia_elem *delem, *next;
+ int val;
+
+ if (dtree == NULL)
+ return;
+
+ return_if_fail(state != NULL);
+
+ if (STATE_CUR(state) == NULL)
+ {
+ mowgli_log("mowgli_patricia_foreach_next(): called again after iteration finished on dtree<%p>", dtree);
+ return;
+ }
+
+ STATE_CUR(state) = STATE_NEXT(state);
+
+ if (STATE_NEXT(state) == NULL)
+ return;
+
+ leaf = STATE_NEXT(state);
+ delem = leaf->parent;
+ val = leaf->parent_val;
+
+ while (delem != NULL)
+ {
+ do
+ next = delem->node.down[val++];
+ while (next == NULL && val < POINTERS_PER_NODE);
+ if (next != NULL)
+ {
+ if (IS_LEAF(next))
+ {
+ /* We will find the original leaf first. */
+ if (&next->leaf != leaf)
+ {
+ if (strcmp(next->leaf.key, leaf->key) < 0)
+ {
+ mowgli_log("mowgli_patricia_foreach_next(): iteration went backwards (libmowgli bug) on dtree<%p>", dtree);
+ STATE_NEXT(state) = NULL;
+ return;
+ }
+ STATE_NEXT(state) = next;
+ return;
+ }
+ }
+ else
+ {
+ delem = next;
+ val = 0;
+ }
+ }
+ while (val >= POINTERS_PER_NODE)
+ {
+ val = delem->node.parent_val;
+ delem = delem->node.parent;
+ if (delem == NULL)
+ break;
+ val++;
+ }
+ }
+ STATE_NEXT(state) = NULL;
+}
+
+/*
+ * mowgli_patricia_elem_find(mowgli_patricia_t *dtree, const char *key)
+ *
+ * Looks up a DTree node by name.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - name of node to lookup
+ *
+ * Outputs:
+ * - on success, the dtree node requested
+ * - on failure, NULL
+ *
+ * Side Effects:
+ * - none
+ */
+struct patricia_leaf *mowgli_patricia_elem_find(mowgli_patricia_t *dict, const char *key)
+{
+ char ckey_store[256];
+ char *ckey_buf = NULL;
+ const char *ckey;
+ union patricia_elem *delem;
+ int val, keylen;
+
+ return_val_if_fail(dict != NULL, NULL);
+ return_val_if_fail(key != NULL, NULL);
+
+ keylen = strlen(key);
+
+ if (dict->canonize_cb == NULL)
+ ckey = key;
+ else
+ {
+ if (keylen >= (int) sizeof(ckey_store))
+ {
+ ckey_buf = mowgli_strdup(key);
+ dict->canonize_cb(ckey_buf);
+ ckey = ckey_buf;
+ }
+ else
+ {
+ mowgli_strlcpy(ckey_store, key, sizeof ckey_store);
+ dict->canonize_cb(ckey_store);
+ ckey = ckey_store;
+ }
+ }
+
+ delem = dict->root;
+ while (delem != NULL && !IS_LEAF(delem))
+ {
+ if (delem->nibnum / 2 < keylen)
+ val = NIBBLE_VAL(ckey, delem->nibnum);
+ else
+ val = 0;
+ delem = delem->node.down[val];
+ }
+ /* Now, if the key is in the tree, delem contains it. */
+ if (delem != NULL && strcmp(delem->leaf.key, ckey))
+ delem = NULL;
+
+ if (ckey_buf != NULL)
+ mowgli_free(ckey_buf);
+
+ return &delem->leaf;
+}
+
+/*
+ * mowgli_patricia_add(mowgli_patricia_t *dtree, const char *key, void *data)
+ *
+ * Creates a new DTree node and binds data to it.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - name for new DTree node
+ * - data to bind to the new DTree node
+ *
+ * Outputs:
+ * - on success, TRUE
+ * - on failure, FALSE
+ *
+ * Side Effects:
+ * - data is inserted into the DTree.
+ */
+struct patricia_leaf *mowgli_patricia_elem_add(mowgli_patricia_t *dict, const char *key, void *data)
+{
+ char *ckey;
+ union patricia_elem *delem, *prev, *newnode;
+ union patricia_elem **place1;
+ int val, keylen;
+ int i, j;
+
+ return_val_if_fail(dict != NULL, FALSE);
+ return_val_if_fail(key != NULL, FALSE);
+ return_val_if_fail(data != NULL, FALSE);
+
+ keylen = strlen(key);
+ ckey = mowgli_strdup(key);
+ if (ckey == NULL)
+ {
+ mowgli_log("major WTF: ckey is NULL, not adding node.");
+ return NULL;
+ }
+ if (dict->canonize_cb != NULL)
+ dict->canonize_cb(ckey);
+
+ prev = NULL;
+ val = POINTERS_PER_NODE + 2; /* trap value */
+ delem = dict->root;
+ while (delem != NULL && !IS_LEAF(delem))
+ {
+ prev = delem;
+ if (delem->nibnum / 2 < keylen)
+ val = NIBBLE_VAL(ckey, delem->nibnum);
+ else
+ val = 0;
+ delem = delem->node.down[val];
+ }
+ /* Now, if the key is in the tree, delem contains it. */
+ if (delem != NULL && !strcmp(delem->leaf.key, ckey))
+ {
+ mowgli_log("Key is already in dict, ignoring duplicate");
+ mowgli_free(ckey);
+ return NULL;
+ }
+
+ if (delem == NULL && prev != NULL)
+ {
+ /* Get a leaf to compare with. */
+ delem = first_leaf(prev);
+ }
+
+ if (delem == NULL)
+ {
+ soft_assert(prev == NULL);
+ soft_assert(dict->count == 0);
+ place1 = &dict->root;
+ *place1 = mowgli_heap_alloc(leaf_heap);
+ return_val_if_fail(*place1 != NULL, NULL);
+ (*place1)->nibnum = -1;
+ (*place1)->leaf.data = data;
+ (*place1)->leaf.key = ckey;
+ (*place1)->leaf.parent = prev;
+ (*place1)->leaf.parent_val = val;
+ dict->count++;
+ return &(*place1)->leaf;
+ }
+
+ /* Find the first nibble where they differ. */
+ for (i = 0; NIBBLE_VAL(ckey, i) == NIBBLE_VAL(delem->leaf.key, i); i++)
+ ;
+ /* Find where to insert the new node. */
+ while (prev != NULL && prev->nibnum > i)
+ {
+ val = prev->node.parent_val;
+ prev = prev->node.parent;
+ }
+ if (prev == NULL || prev->nibnum < i)
+ {
+ /* Insert new node below prev */
+ newnode = mowgli_heap_alloc(node_heap);
+ return_val_if_fail(newnode != NULL, NULL);
+ newnode->nibnum = i;
+ newnode->node.parent = prev;
+ newnode->node.parent_val = val;
+ for (j = 0; j < POINTERS_PER_NODE; j++)
+ newnode->node.down[j] = NULL;
+ if (prev == NULL)
+ {
+ newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = dict->root;
+ if (IS_LEAF(dict->root))
+ {
+ dict->root->leaf.parent = newnode;
+ dict->root->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
+ }
+ else
+ {
+ soft_assert(dict->root->nibnum > i);
+ dict->root->node.parent = newnode;
+ dict->root->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
+ }
+ dict->root = newnode;
+ }
+ else
+ {
+ newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = prev->node.down[val];
+ if (IS_LEAF(prev->node.down[val]))
+ {
+ prev->node.down[val]->leaf.parent = newnode;
+ prev->node.down[val]->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
+ }
+ else
+ {
+ prev->node.down[val]->node.parent = newnode;
+ prev->node.down[val]->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
+ }
+ prev->node.down[val] = newnode;
+ }
+ }
+ else
+ {
+ /* This nibble is already checked. */
+ soft_assert(prev->nibnum == i);
+ newnode = prev;
+ }
+ val = NIBBLE_VAL(ckey, i);
+ place1 = &newnode->node.down[val];
+ soft_assert(*place1 == NULL);
+ *place1 = mowgli_heap_alloc(leaf_heap);
+ return_val_if_fail(*place1 != NULL, NULL);
+ (*place1)->nibnum = -1;
+ (*place1)->leaf.data = data;
+ (*place1)->leaf.key = ckey;
+ (*place1)->leaf.parent = newnode;
+ (*place1)->leaf.parent_val = val;
+ dict->count++;
+ return &(*place1)->leaf;
+}
+
+mowgli_boolean_t mowgli_patricia_add(mowgli_patricia_t *dict, const char *key, void *data)
+{
+ return (mowgli_patricia_elem_add(dict, key, data) != NULL) ? TRUE : FALSE;
+}
+
+/*
+ * mowgli_patricia_delete(mowgli_patricia_t *dtree, const char *key)
+ *
+ * Deletes data from a patricia tree.
+ *
+ * Inputs:
+ * - patricia 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 *mowgli_patricia_delete(mowgli_patricia_t *dict, const char *key)
+{
+ void *data;
+ struct patricia_leaf *leaf;
+
+ leaf = mowgli_patricia_elem_find(dict, key);
+ if (leaf == NULL)
+ return NULL;
+
+ data = leaf->data;
+ mowgli_patricia_elem_delete(dict, leaf);
+ return data;
+}
+
+void mowgli_patricia_elem_delete(mowgli_patricia_t *dict, struct patricia_leaf *leaf)
+{
+ union patricia_elem *delem, *prev, *next;
+ int val, i, used;
+
+ return_if_fail(dict != NULL);
+ return_if_fail(leaf != NULL);
+
+ delem = (union patricia_elem *)leaf;
+
+ val = delem->leaf.parent_val;
+ prev = delem->leaf.parent;
+
+ mowgli_free(delem->leaf.key);
+ mowgli_heap_free(leaf_heap, delem);
+
+ if (prev != NULL)
+ {
+ prev->node.down[val] = NULL;
+
+ /* Leaf is gone, now consider the node it was in. */
+ delem = prev;
+
+ used = -1;
+ for (i = 0; i < POINTERS_PER_NODE; i++)
+ if (delem->node.down[i] != NULL)
+ used = used == -1 ? i : -2;
+ soft_assert(used == -2 || used >= 0);
+ if (used >= 0)
+ {
+ /* Only one pointer in this node, remove it.
+ * Replace the pointer that pointed to it by
+ * the sole pointer in it.
+ */
+ next = delem->node.down[used];
+ val = delem->node.parent_val;
+ prev = delem->node.parent;
+ if (prev != NULL)
+ prev->node.down[val] = next;
+ else
+ dict->root = next;
+ if (IS_LEAF(next))
+ next->leaf.parent = prev, next->leaf.parent_val = val;
+ else
+ next->node.parent = prev, next->node.parent_val = val;
+ mowgli_heap_free(node_heap, delem);
+ }
+ }
+ else
+ {
+ /* This was the last leaf. */
+ dict->root = NULL;
+ }
+
+ dict->count--;
+ if (dict->count == 0)
+ {
+ soft_assert(dict->root == NULL);
+ dict->root = NULL;
+ }
+}
+
+/*
+ * mowgli_patricia_retrieve(mowgli_patricia_t *dtree, const char *key)
+ *
+ * Retrieves data from a patricia.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - name of node to lookup
+ *
+ * Outputs:
+ * - on success, the data bound to the DTree node.
+ * - on failure, NULL
+ *
+ * Side Effects:
+ * - none
+ */
+void *mowgli_patricia_retrieve(mowgli_patricia_t *dtree, const char *key)
+{
+ struct patricia_leaf *delem = mowgli_patricia_elem_find(dtree, key);
+
+ if (delem != NULL)
+ return delem->data;
+
+ return NULL;
+}
+
+const char *mowgli_patricia_elem_get_key(struct patricia_leaf *leaf)
+{
+ return_val_if_fail(leaf != NULL, NULL);
+
+ return leaf->key;
+}
+
+void mowgli_patricia_elem_set_data(struct patricia_leaf *leaf, void *data)
+{
+ return_if_fail(leaf != NULL);
+
+ leaf->data = data;
+}
+
+void *mowgli_patricia_elem_get_data(struct patricia_leaf *leaf)
+{
+ return_val_if_fail(leaf != NULL, NULL);
+
+ return leaf->data;
+}
+
+/*
+ * mowgli_patricia_size(mowgli_patricia_t *dict)
+ *
+ * Returns the size of a patricia.
+ *
+ * Inputs:
+ * - patricia tree object
+ *
+ * Outputs:
+ * - size of patricia
+ *
+ * Side Effects:
+ * - none
+ */
+unsigned int mowgli_patricia_size(mowgli_patricia_t *dict)
+{
+ return_val_if_fail(dict != NULL, 0);
+
+ return dict->count;
+}
+
+/* returns the sum of the depths of the subtree rooted in delem at depth depth */
+/* there is no need for this to be recursive, but it is easier... */
+static int
+stats_recurse(union patricia_elem *delem, int depth, int *pmaxdepth)
+{
+ int result = 0;
+ int val;
+ union patricia_elem *next;
+
+ if (depth > *pmaxdepth)
+ *pmaxdepth = depth;
+ if (depth == 0)
+ {
+ if (IS_LEAF(delem))
+ {
+ soft_assert(delem->leaf.parent == NULL);
+ }
+ else
+ {
+ soft_assert(delem->node.parent == NULL);
+ }
+ }
+ if (IS_LEAF(delem))
+ return depth;
+ for (val = 0; val < POINTERS_PER_NODE; val++)
+ {
+ next = delem->node.down[val];
+ if (next == NULL)
+ continue;
+ result += stats_recurse(next, depth + 1, pmaxdepth);
+ if (IS_LEAF(next))
+ {
+ soft_assert(next->leaf.parent == delem);
+ soft_assert(next->leaf.parent_val == val);
+ }
+ else
+ {
+ soft_assert(next->node.parent == delem);
+ soft_assert(next->node.parent_val == val);
+ soft_assert(next->node.nibnum > delem->node.nibnum);
+ }
+ }
+ return result;
+}
+
+/*
+ * mowgli_patricia_stats(mowgli_patricia_t *dict, void (*cb)(const char *line, void *privdata), void *privdata)
+ *
+ * Returns the size of a patricia.
+ *
+ * Inputs:
+ * - patricia tree object
+ * - callback
+ * - data for callback
+ *
+ * Outputs:
+ * - none
+ *
+ * Side Effects:
+ * - callback called with stats text
+ */
+void mowgli_patricia_stats(mowgli_patricia_t *dict, void (*cb)(const char *line, void *privdata), void *privdata)
+{
+ char str[256];
+ int sum, maxdepth;
+
+ return_if_fail(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;
+ if (dict->count > 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);
+ }
+ else
+ snprintf(str, sizeof str, "Depth sum 0 Avg depth 0 Max depth 0");
+ cb(str, privdata);
+ return;
+}
diff --git a/src/libmowgli/container/patricia.h b/src/libmowgli/container/patricia.h
new file mode 100644
index 0000000..417ba62
--- /dev/null
+++ b/src/libmowgli/container/patricia.h
@@ -0,0 +1,155 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * patricia.h: Dictionary-based storage.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ * Copyright (c) 2007-2008 Jilles Tjoelker <jilles -at- stack.nl>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 __MOWGLI_PATRICIA_H__
+#define __MOWGLI_PATRICIA_H__
+
+struct mowgli_patricia_; /* defined in src/patricia.c */
+struct mowgli_patricia_elem_; /* defined in src/patricia.c */
+
+typedef struct mowgli_patricia_ mowgli_patricia_t;
+typedef struct mowgli_patricia_elem_ mowgli_patricia_elem_t;
+
+/*
+ * mowgli_patricia_iteration_state_t, private.
+ */
+struct mowgli_patricia_iteration_state_
+{
+ mowgli_patricia_elem_t *cur, *next;
+ void *pspare[4];
+ int ispare[4];
+};
+
+typedef struct mowgli_patricia_iteration_state_ mowgli_patricia_iteration_state_t;
+
+/*
+ * this is a convenience macro for inlining iteration of dictionaries.
+ */
+#define MOWGLI_PATRICIA_FOREACH(element, state, dict) for (mowgli_patricia_foreach_start((dict), (state)); (element = mowgli_patricia_foreach_cur((dict), (state))); mowgli_patricia_foreach_next((dict), (state)))
+
+/*
+ * mowgli_patricia_create() creates a new patricia tree of the defined resolution.
+ * compare_cb is the canonizing function.
+ */
+
+/* defined if this version of Mowgli allows canonize_cb to be NULL */
+#define MOWGLI_PATRICIA_ALLOWS_NULL_CANONIZE
+
+extern mowgli_patricia_t *mowgli_patricia_create(void (*canonize_cb)(char *key));
+
+/*
+ * mowgli_patricia_shutdown() deallocates all heaps used in patricia trees. This is
+ * useful on embedded devices with little memory, and/or when you know you won't need
+ * any more patricia trees.
+ */
+extern void mowgli_patricia_shutdown(void);
+
+/*
+ * mowgli_patricia_destroy() destroys all entries in a dtree, and also optionally calls
+ * a defined callback function to destroy any data attached to it.
+ */
+extern void mowgli_patricia_destroy(mowgli_patricia_t *dtree,
+ void (*destroy_cb)(const char *key, void *data, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_patricia_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 mowgli_patricia_foreach(mowgli_patricia_t *dtree,
+ int (*foreach_cb)(const char *key, void *data, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_patricia_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 *mowgli_patricia_search(mowgli_patricia_t *dtree,
+ void *(*foreach_cb)(const char *key, void *data, void *privdata),
+ void *privdata);
+
+/*
+ * mowgli_patricia_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 mowgli_patricia_foreach_start(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state);
+
+/*
+ * mowgli_patricia_foreach_cur() returns the current element of the iteration,
+ * or NULL if there are no more elements.
+ */
+extern void *mowgli_patricia_foreach_cur(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state);
+
+/*
+ * mowgli_patricia_foreach_next() moves to the next element.
+ */
+extern void mowgli_patricia_foreach_next(mowgli_patricia_t *dtree,
+ mowgli_patricia_iteration_state_t *state);
+
+/*
+ * mowgli_patricia_add() adds a key->value entry to the patricia tree.
+ */
+extern mowgli_boolean_t mowgli_patricia_add(mowgli_patricia_t *dtree, const char *key, void *data);
+
+/*
+ * mowgli_patricia_find() returns data from a dtree for key 'key'.
+ */
+extern void *mowgli_patricia_retrieve(mowgli_patricia_t *dtree, const char *key);
+
+/*
+ * mowgli_patricia_delete() deletes a key->value entry from the patricia tree.
+ */
+extern void *mowgli_patricia_delete(mowgli_patricia_t *dtree, const char *key);
+
+/* Low-level functions */
+mowgli_patricia_elem_t *mowgli_patricia_elem_add(mowgli_patricia_t *dtree, const char *key, void *data);
+mowgli_patricia_elem_t *mowgli_patricia_elem_find(mowgli_patricia_t *dtree, const char *key);
+void mowgli_patricia_elem_delete(mowgli_patricia_t *dtree, mowgli_patricia_elem_t *elem);
+const char *mowgli_patricia_elem_get_key(mowgli_patricia_elem_t *elem);
+void mowgli_patricia_elem_set_data(mowgli_patricia_elem_t *elem, void *data);
+void *mowgli_patricia_elem_get_data(mowgli_patricia_elem_t *elem);
+
+unsigned int mowgli_patricia_size(mowgli_patricia_t *dict);
+void mowgli_patricia_stats(mowgli_patricia_t *dict, void (*cb)(const char *line, void *privdata), void *privdata);
+
+#endif
diff --git a/src/libmowgli/container/queue.c b/src/libmowgli/container/queue.c
new file mode 100644
index 0000000..39150d3
--- /dev/null
+++ b/src/libmowgli/container/queue.c
@@ -0,0 +1,231 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * queue.c: Double-ended queues, also known as deque.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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 "mowgli.h"
+
+static mowgli_heap_t *mowgli_queue_heap = NULL;
+
+void
+mowgli_queue_bootstrap(void)
+{
+ mowgli_queue_heap = mowgli_heap_create(sizeof(mowgli_queue_t), 256, BH_NOW);
+
+ if (mowgli_queue_heap == NULL)
+ mowgli_log("mowgli_queue_heap was not created, expect problems.");
+}
+
+mowgli_queue_t *
+mowgli_queue_shift(mowgli_queue_t *head, void *data)
+{
+ mowgli_queue_t *out = mowgli_heap_alloc(mowgli_queue_heap);
+
+ return_val_if_fail(head != NULL, NULL);
+
+ out->next = head;
+ out->data = data;
+
+ if (head != NULL)
+ {
+ out->prev = head->prev;
+
+ if (out->prev != NULL)
+ out->prev->next = out;
+
+ head->prev = out;
+ }
+
+ return out;
+}
+
+mowgli_queue_t *
+mowgli_queue_push(mowgli_queue_t *head, void *data)
+{
+ mowgli_queue_t *out = mowgli_heap_alloc(mowgli_queue_heap);
+
+ return_val_if_fail(head != NULL, NULL);
+
+ out->prev = head;
+ out->data = data;
+
+ if (head != NULL)
+ head->next = out;
+
+ return out;
+}
+
+mowgli_queue_t *
+mowgli_queue_remove(mowgli_queue_t *head)
+{
+ mowgli_queue_t *out;
+
+ return_val_if_fail(head != NULL, NULL);
+
+ if (head->prev != NULL)
+ head->prev->next = head->next;
+
+ if (head->next != NULL)
+ head->next->prev = head->prev;
+
+ out = head->prev != NULL ? head->prev : head->next;
+
+ mowgli_heap_free(mowgli_queue_heap, head);
+
+ return out;
+}
+
+mowgli_queue_t *
+mowgli_queue_find(mowgli_queue_t *head, void *data)
+{
+ mowgli_queue_t *n;
+
+ return_val_if_fail(head != NULL, NULL);
+
+ for (n = head; n != NULL; n = n->next)
+ if (n->data == data)
+ return n;
+
+ return NULL;
+}
+
+mowgli_queue_t *
+mowgli_queue_remove_data(mowgli_queue_t *head, void *data)
+{
+ mowgli_queue_t *n = mowgli_queue_find(head, data);
+
+ return_val_if_fail(head != NULL, NULL);
+
+ if (n != NULL)
+ return mowgli_queue_remove(n);
+
+ return NULL;
+}
+
+void
+mowgli_queue_destroy(mowgli_queue_t *head)
+{
+ mowgli_queue_t *n, *n2;
+
+ return_if_fail(head != NULL);
+
+ for (n = head, n2 = n ? n->next : NULL; n != NULL; n = n2, n2 = n ? n->next : NULL)
+ mowgli_queue_remove(n);
+}
+
+mowgli_queue_t *
+mowgli_queue_skip(mowgli_queue_t *head, int nodes)
+{
+ mowgli_queue_t *n;
+ int iter;
+
+ return_val_if_fail(head != NULL, NULL);
+
+ for (iter = 0, n = head; n != NULL && iter < nodes; n = n->next, iter++);
+
+ return n;
+}
+
+mowgli_queue_t *
+mowgli_queue_rewind(mowgli_queue_t *head, int nodes)
+{
+ mowgli_queue_t *n;
+ int iter;
+
+ return_val_if_fail(head != NULL, NULL);
+
+ for (iter = 0, n = head; n != NULL && iter < nodes; n = n->prev, iter++);
+
+ return n;
+}
+
+mowgli_queue_t *
+mowgli_queue_head(mowgli_queue_t *n)
+{
+ mowgli_queue_t *tn;
+
+ return_val_if_fail(n != NULL, NULL);
+
+ for (tn = n; tn != NULL && tn->prev != NULL; tn = tn->prev);
+
+ return tn;
+}
+
+mowgli_queue_t *
+mowgli_queue_tail(mowgli_queue_t *n)
+{
+ mowgli_queue_t *tn;
+
+ return_val_if_fail(n != NULL, NULL);
+
+ for (tn = n; tn != NULL && tn->next != NULL; tn = tn->next);
+
+ return tn;
+}
+
+void *
+mowgli_queue_pop_head(mowgli_queue_t **n)
+{
+ mowgli_queue_t *tn;
+ void *out;
+
+ return_val_if_fail(n != NULL, NULL);
+ return_val_if_fail(*n != NULL, NULL);
+
+ tn = *n;
+ out = tn->data;
+ *n = tn->next;
+
+ mowgli_queue_remove(tn);
+
+ return out;
+}
+
+void *
+mowgli_queue_pop_tail(mowgli_queue_t **n)
+{
+ mowgli_queue_t *tn;
+ void *out;
+
+ return_val_if_fail(n != NULL, NULL);
+ return_val_if_fail(*n != NULL, NULL);
+
+ tn = *n;
+ out = tn->data;
+ *n = tn->prev;
+
+ mowgli_queue_remove(tn);
+
+ return out;
+}
+
+int
+mowgli_queue_length(mowgli_queue_t *head)
+{
+ int iter;
+ mowgli_queue_t *n;
+
+ return_val_if_fail(head != NULL, -1);
+
+ for (n = head, iter = 0; n != NULL; n = n->next, iter++);
+
+ return iter;
+}
diff --git a/src/libmowgli/container/queue.h b/src/libmowgli/container/queue.h
new file mode 100644
index 0000000..7f8d995
--- /dev/null
+++ b/src/libmowgli/container/queue.h
@@ -0,0 +1,44 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * queue.h: Double-ended queues, also known as deque.
+ *
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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 __MOWGLI_QUEUE_H__
+#define __MOWGLI_QUEUE_H__
+
+typedef mowgli_iterator_t mowgli_queue_t;
+
+extern void mowgli_queue_bootstrap(void);
+extern mowgli_queue_t *mowgli_queue_push(mowgli_queue_t *head, void *data);
+extern mowgli_queue_t *mowgli_queue_shift(mowgli_queue_t *head, void *data);
+extern mowgli_queue_t *mowgli_queue_remove(mowgli_queue_t *head);
+extern mowgli_queue_t *mowgli_queue_find(mowgli_queue_t *head, void *data);
+extern mowgli_queue_t *mowgli_queue_remove_data(mowgli_queue_t *head, void *data);
+extern void mowgli_queue_destroy(mowgli_queue_t *head);
+extern mowgli_queue_t *mowgli_queue_skip(mowgli_queue_t *head, int amt);
+extern mowgli_queue_t *mowgli_queue_rewind(mowgli_queue_t *head, int amt);
+extern mowgli_queue_t *mowgli_queue_head(mowgli_queue_t *n);
+extern mowgli_queue_t *mowgli_queue_tail(mowgli_queue_t *n);
+extern void *mowgli_queue_pop_head(mowgli_queue_t **n);
+extern void *mowgli_queue_pop_tail(mowgli_queue_t **n);
+extern int mowgli_queue_length(mowgli_queue_t *head);
+
+#endif