aboutsummaryrefslogtreecommitdiff
path: root/lib/cpluff/kazlib
diff options
context:
space:
mode:
authoralcoheca <alcoheca@svn>2010-03-26 13:35:35 +0000
committeralcoheca <alcoheca@svn>2010-03-26 13:35:35 +0000
commitb632a42d5cacdda7bb07a3fee995a4f0de1deba9 (patch)
treeb174ecd7ce4830785293fa6d18ab65d30c0cc375 /lib/cpluff/kazlib
parent5ccef76d00e1dc22d6ae5d1c08172630f5972e58 (diff)
copied cpluff-0.1.3 from vendor branch
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@28834 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'lib/cpluff/kazlib')
-rw-r--r--lib/cpluff/kazlib/hash.c1041
-rw-r--r--lib/cpluff/kazlib/hash.h248
-rw-r--r--lib/cpluff/kazlib/list.c935
-rw-r--r--lib/cpluff/kazlib/list.h162
4 files changed, 2386 insertions, 0 deletions
diff --git a/lib/cpluff/kazlib/hash.c b/lib/cpluff/kazlib/hash.c
new file mode 100644
index 0000000000..4cc281fb0d
--- /dev/null
+++ b/lib/cpluff/kazlib/hash.c
@@ -0,0 +1,1041 @@
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+#define HASH_IMPLEMENTATION
+#include "hash.h"
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $";
+#endif
+
+#define INIT_BITS 6
+#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */
+#define INIT_MASK ((INIT_SIZE) - 1)
+
+#define next hash_next
+#define key hash_key
+#define data hash_data
+#define hkey hash_hkey
+
+#define table hash_table
+#define nchains hash_nchains
+#define nodecount hash_nodecount
+#define maxcount hash_maxcount
+#define highmark hash_highmark
+#define lowmark hash_lowmark
+#define compare hash_compare
+#define function hash_function
+#define allocnode hash_allocnode
+#define freenode hash_freenode
+#define context hash_context
+#define mask hash_mask
+#define dynamic hash_dynamic
+
+#define table hash_table
+#define chain hash_chain
+
+static hnode_t *hnode_alloc(void *context);
+static void hnode_free(hnode_t *node, void *context);
+static hash_val_t hash_fun_default(const void *key);
+static int hash_comp_default(const void *key1, const void *key2);
+
+CP_HIDDEN int hash_val_t_bit;
+
+/*
+ * Compute the number of bits in the hash_val_t type. We know that hash_val_t
+ * is an unsigned integral type. Thus the highest value it can hold is a
+ * Mersenne number (power of two, less one). We initialize a hash_val_t
+ * object with this value and then shift bits out one by one while counting.
+ * Notes:
+ * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power
+ * of two. This means that its binary representation consists of all one
+ * bits, and hence ``val'' is initialized to all one bits.
+ * 2. While bits remain in val, we increment the bit count and shift it to the
+ * right, replacing the topmost bit by zero.
+ */
+
+static void compute_bits(void)
+{
+ hash_val_t val = HASH_VAL_T_MAX; /* 1 */
+ int bits = 0;
+
+ while (val) { /* 2 */
+ bits++;
+ val >>= 1;
+ }
+
+ hash_val_t_bit = bits;
+}
+
+/*
+ * Verify whether the given argument is a power of two.
+ */
+
+static int is_power_of_two(hash_val_t arg)
+{
+ if (arg == 0)
+ return 0;
+ while ((arg & 1) == 0)
+ arg >>= 1;
+ return (arg == 1);
+}
+
+/*
+ * Compute a shift amount from a given table size
+ */
+
+static hash_val_t compute_mask(hashcount_t size)
+{
+ assert (is_power_of_two(size));
+ assert (size >= 2);
+
+ return size - 1;
+}
+
+/*
+ * Initialize the table of pointers to null.
+ */
+
+static void clear_table(hash_t *hash)
+{
+ hash_val_t i;
+
+ for (i = 0; i < hash->nchains; i++)
+ hash->table[i] = NULL;
+}
+
+/*
+ * Double the size of a dynamic table. This works as follows. Each chain splits
+ * into two adjacent chains. The shift amount increases by one, exposing an
+ * additional bit of each hashed key. For each node in the original chain, the
+ * value of this newly exposed bit will decide which of the two new chains will
+ * receive the node: if the bit is 1, the chain with the higher index will have
+ * the node, otherwise the lower chain will receive the node. In this manner,
+ * the hash table will continue to function exactly as before without having to
+ * rehash any of the keys.
+ * Notes:
+ * 1. Overflow check.
+ * 2. The new number of chains is twice the old number of chains.
+ * 3. The new mask is one bit wider than the previous, revealing a
+ * new bit in all hashed keys.
+ * 4. Allocate a new table of chain pointers that is twice as large as the
+ * previous one.
+ * 5. If the reallocation was successful, we perform the rest of the growth
+ * algorithm, otherwise we do nothing.
+ * 6. The exposed_bit variable holds a mask with which each hashed key can be
+ * AND-ed to test the value of its newly exposed bit.
+ * 7. Now loop over each chain in the table and sort its nodes into two
+ * chains based on the value of each node's newly exposed hash bit.
+ * 8. The low chain replaces the current chain. The high chain goes
+ * into the corresponding sister chain in the upper half of the table.
+ * 9. We have finished dealing with the chains and nodes. We now update
+ * the various bookeeping fields of the hash structure.
+ */
+
+static void grow_table(hash_t *hash)
+{
+ hnode_t **newtable;
+
+ assert (2 * hash->nchains > hash->nchains); /* 1 */
+
+ newtable = realloc(hash->table,
+ sizeof *newtable * hash->nchains * 2); /* 4 */
+
+ if (newtable) { /* 5 */
+ hash_val_t mask = (hash->mask << 1) | 1; /* 3 */
+ hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */
+ hash_val_t chain;
+
+ assert (mask != hash->mask);
+
+ for (chain = 0; chain < hash->nchains; chain++) { /* 7 */
+ hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next;
+
+ for (hptr = newtable[chain]; hptr != 0; hptr = next) {
+ next = hptr->next;
+
+ if (hptr->hkey & exposed_bit) {
+ hptr->next = high_chain;
+ high_chain = hptr;
+ } else {
+ hptr->next = low_chain;
+ low_chain = hptr;
+ }
+ }
+
+ newtable[chain] = low_chain; /* 8 */
+ newtable[chain + hash->nchains] = high_chain;
+ }
+
+ hash->table = newtable; /* 9 */
+ hash->mask = mask;
+ hash->nchains *= 2;
+ hash->lowmark *= 2;
+ hash->highmark *= 2;
+ }
+ assert (hash_verify(hash));
+}
+
+/*
+ * Cut a table size in half. This is done by folding together adjacent chains
+ * and populating the lower half of the table with these chains. The chains are
+ * simply spliced together. Once this is done, the whole table is reallocated
+ * to a smaller object.
+ * Notes:
+ * 1. It is illegal to have a hash table with one slot. This would mean that
+ * hash->shift is equal to hash_val_t_bit, an illegal shift value.
+ * Also, other things could go wrong, such as hash->lowmark becoming zero.
+ * 2. Looping over each pair of sister chains, the low_chain is set to
+ * point to the head node of the chain in the lower half of the table,
+ * and high_chain points to the head node of the sister in the upper half.
+ * 3. The intent here is to compute a pointer to the last node of the
+ * lower chain into the low_tail variable. If this chain is empty,
+ * low_tail ends up with a null value.
+ * 4. If the lower chain is not empty, we simply tack the upper chain onto it.
+ * If the upper chain is a null pointer, nothing happens.
+ * 5. Otherwise if the lower chain is empty but the upper one is not,
+ * If the low chain is empty, but the high chain is not, then the
+ * high chain is simply transferred to the lower half of the table.
+ * 6. Otherwise if both chains are empty, there is nothing to do.
+ * 7. All the chain pointers are in the lower half of the table now, so
+ * we reallocate it to a smaller object. This, of course, invalidates
+ * all pointer-to-pointers which reference into the table from the
+ * first node of each chain.
+ * 8. Though it's unlikely, the reallocation may fail. In this case we
+ * pretend that the table _was_ reallocated to a smaller object.
+ * 9. Finally, update the various table parameters to reflect the new size.
+ */
+
+static void shrink_table(hash_t *hash)
+{
+ hash_val_t chain, nchains;
+ hnode_t **newtable, *low_tail, *low_chain, *high_chain;
+
+ assert (hash->nchains >= 2); /* 1 */
+ nchains = hash->nchains / 2;
+
+ for (chain = 0; chain < nchains; chain++) {
+ low_chain = hash->table[chain]; /* 2 */
+ high_chain = hash->table[chain + nchains];
+ for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next)
+ ; /* 3 */
+ if (low_chain != 0) /* 4 */
+ low_tail->next = high_chain;
+ else if (high_chain != 0) /* 5 */
+ hash->table[chain] = high_chain;
+ else
+ assert (hash->table[chain] == NULL); /* 6 */
+ }
+ newtable = realloc(hash->table,
+ sizeof *newtable * nchains); /* 7 */
+ if (newtable) /* 8 */
+ hash->table = newtable;
+ hash->mask >>= 1; /* 9 */
+ hash->nchains = nchains;
+ hash->lowmark /= 2;
+ hash->highmark /= 2;
+ assert (hash_verify(hash));
+}
+
+
+/*
+ * Create a dynamic hash table. Both the hash table structure and the table
+ * itself are dynamically allocated. Furthermore, the table is extendible in
+ * that it will automatically grow as its load factor increases beyond a
+ * certain threshold.
+ * Notes:
+ * 1. If the number of bits in the hash_val_t type has not been computed yet,
+ * we do so here, because this is likely to be the first function that the
+ * user calls.
+ * 2. Allocate a hash table control structure.
+ * 3. If a hash table control structure is successfully allocated, we
+ * proceed to initialize it. Otherwise we return a null pointer.
+ * 4. We try to allocate the table of hash chains.
+ * 5. If we were able to allocate the hash chain table, we can finish
+ * initializing the hash structure and the table. Otherwise, we must
+ * backtrack by freeing the hash structure.
+ * 6. INIT_SIZE should be a power of two. The high and low marks are always set
+ * to be twice the table size and half the table size respectively. When the
+ * number of nodes in the table grows beyond the high size (beyond load
+ * factor 2), it will double in size to cut the load factor down to about
+ * about 1. If the table shrinks down to or beneath load factor 0.5,
+ * it will shrink, bringing the load up to about 1. However, the table
+ * will never shrink beneath INIT_SIZE even if it's emptied.
+ * 7. This indicates that the table is dynamically allocated and dynamically
+ * resized on the fly. A table that has this value set to zero is
+ * assumed to be statically allocated and will not be resized.
+ * 8. The table of chains must be properly reset to all null pointers.
+ */
+
+CP_HIDDEN hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun,
+ hash_fun_t hashfun)
+{
+ hash_t *hash;
+
+ if (hash_val_t_bit == 0) /* 1 */
+ compute_bits();
+
+ hash = malloc(sizeof *hash); /* 2 */
+
+ if (hash) { /* 3 */
+ hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */
+ if (hash->table) { /* 5 */
+ hash->nchains = INIT_SIZE; /* 6 */
+ hash->highmark = INIT_SIZE * 2;
+ hash->lowmark = INIT_SIZE / 2;
+ hash->nodecount = 0;
+ hash->maxcount = maxcount;
+ hash->compare = compfun ? compfun : hash_comp_default;
+ hash->function = hashfun ? hashfun : hash_fun_default;
+ hash->allocnode = hnode_alloc;
+ hash->freenode = hnode_free;
+ hash->context = NULL;
+ hash->mask = INIT_MASK;
+ hash->dynamic = 1; /* 7 */
+ clear_table(hash); /* 8 */
+ assert (hash_verify(hash));
+ return hash;
+ }
+ free(hash);
+ }
+
+ return NULL;
+}
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+CP_HIDDEN void hash_set_allocator(hash_t *hash, hnode_alloc_t al,
+ hnode_free_t fr, void *context)
+{
+ assert (hash_count(hash) == 0);
+ assert ((al == 0 && fr == 0) || (al != 0 && fr != 0));
+
+ hash->allocnode = al ? al : hnode_alloc;
+ hash->freenode = fr ? fr : hnode_free;
+ hash->context = context;
+}
+
+/*
+ * Free every node in the hash using the hash->freenode() function pointer, and
+ * cause the hash to become empty.
+ */
+
+CP_HIDDEN void hash_free_nodes(hash_t *hash)
+{
+ hscan_t hs;
+ hnode_t *node;
+ hash_scan_begin(&hs, hash);
+ while ((node = hash_scan_next(&hs))) {
+ hash_scan_delete(hash, node);
+ hash->freenode(node, hash->context);
+ }
+ hash->nodecount = 0;
+ clear_table(hash);
+}
+
+/*
+ * Obsolescent function for removing all nodes from a table,
+ * freeing them and then freeing the table all in one step.
+ */
+
+CP_HIDDEN void hash_free(hash_t *hash)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+ assert ("call to obsolescent function hash_free()" && 0);
+#endif
+ hash_free_nodes(hash);
+ hash_destroy(hash);
+}
+
+/*
+ * Free a dynamic hash table structure.
+ */
+
+CP_HIDDEN void hash_destroy(hash_t *hash)
+{
+ assert (hash_val_t_bit != 0);
+ assert (hash_isempty(hash));
+ free(hash->table);
+ free(hash);
+}
+
+/*
+ * Initialize a user supplied hash structure. The user also supplies a table of
+ * chains which is assigned to the hash structure. The table is static---it
+ * will not grow or shrink.
+ * 1. See note 1. in hash_create().
+ * 2. The user supplied array of pointers hopefully contains nchains nodes.
+ * 3. See note 7. in hash_create().
+ * 4. We must dynamically compute the mask from the given power of two table
+ * size.
+ * 5. The user supplied table can't be assumed to contain null pointers,
+ * so we reset it here.
+ */
+
+CP_HIDDEN hash_t *hash_init(hash_t *hash, hashcount_t maxcount,
+ hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table,
+ hashcount_t nchains)
+{
+ if (hash_val_t_bit == 0) /* 1 */
+ compute_bits();
+
+ assert (is_power_of_two(nchains));
+
+ hash->table = table; /* 2 */
+ hash->nchains = nchains;
+ hash->nodecount = 0;
+ hash->maxcount = maxcount;
+ hash->compare = compfun ? compfun : hash_comp_default;
+ hash->function = hashfun ? hashfun : hash_fun_default;
+ hash->dynamic = 0; /* 3 */
+ hash->mask = compute_mask(nchains); /* 4 */
+ clear_table(hash); /* 5 */
+
+ assert (hash_verify(hash));
+
+ return hash;
+}
+
+/*
+ * Reset the hash scanner so that the next element retrieved by
+ * hash_scan_next() shall be the first element on the first non-empty chain.
+ * Notes:
+ * 1. Locate the first non empty chain.
+ * 2. If an empty chain is found, remember which one it is and set the next
+ * pointer to refer to its first element.
+ * 3. Otherwise if a chain is not found, set the next pointer to NULL
+ * so that hash_scan_next() shall indicate failure.
+ */
+
+CP_HIDDEN void hash_scan_begin(hscan_t *scan, hash_t *hash)
+{
+ hash_val_t nchains = hash->nchains;
+ hash_val_t chain;
+
+ scan->table = hash;
+
+ /* 1 */
+
+ for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++)
+ ;
+
+ if (chain < nchains) { /* 2 */
+ scan->chain = chain;
+ scan->next = hash->table[chain];
+ } else { /* 3 */
+ scan->next = NULL;
+ }
+}
+
+/*
+ * Retrieve the next node from the hash table, and update the pointer
+ * for the next invocation of hash_scan_next().
+ * Notes:
+ * 1. Remember the next pointer in a temporary value so that it can be
+ * returned.
+ * 2. This assertion essentially checks whether the module has been properly
+ * initialized. The first point of interaction with the module should be
+ * either hash_create() or hash_init(), both of which set hash_val_t_bit to
+ * a non zero value.
+ * 3. If the next pointer we are returning is not NULL, then the user is
+ * allowed to call hash_scan_next() again. We prepare the new next pointer
+ * for that call right now. That way the user is allowed to delete the node
+ * we are about to return, since we will no longer be needing it to locate
+ * the next node.
+ * 4. If there is a next node in the chain (next->next), then that becomes the
+ * new next node, otherwise ...
+ * 5. We have exhausted the current chain, and must locate the next subsequent
+ * non-empty chain in the table.
+ * 6. If a non-empty chain is found, the first element of that chain becomes
+ * the new next node. Otherwise there is no new next node and we set the
+ * pointer to NULL so that the next time hash_scan_next() is called, a null
+ * pointer shall be immediately returned.
+ */
+
+
+CP_HIDDEN hnode_t *hash_scan_next(hscan_t *scan)
+{
+ hnode_t *next = scan->next; /* 1 */
+ hash_t *hash = scan->table;
+ hash_val_t chain = scan->chain + 1;
+ hash_val_t nchains = hash->nchains;
+
+ assert (hash_val_t_bit != 0); /* 2 */
+
+ if (next) { /* 3 */
+ if (next->next) { /* 4 */
+ scan->next = next->next;
+ } else {
+ while (chain < nchains && hash->table[chain] == 0) /* 5 */
+ chain++;
+ if (chain < nchains) { /* 6 */
+ scan->chain = chain;
+ scan->next = hash->table[chain];
+ } else {
+ scan->next = NULL;
+ }
+ }
+ }
+ return next;
+}
+
+/*
+ * Insert a node into the hash table.
+ * Notes:
+ * 1. It's illegal to insert more than the maximum number of nodes. The client
+ * should verify that the hash table is not full before attempting an
+ * insertion.
+ * 2. The same key may not be inserted into a table twice.
+ * 3. If the table is dynamic and the load factor is already at >= 2,
+ * grow the table.
+ * 4. We take the bottom N bits of the hash value to derive the chain index,
+ * where N is the base 2 logarithm of the size of the hash table.
+ */
+
+CP_HIDDEN void hash_insert(hash_t *hash, hnode_t *node, const void *key)
+{
+ hash_val_t hkey, chain;
+
+ assert (hash_val_t_bit != 0);
+ assert (node->next == NULL);
+ assert (hash->nodecount < hash->maxcount); /* 1 */
+ assert (hash_lookup(hash, key) == NULL); /* 2 */
+
+ if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */
+ grow_table(hash);
+
+ hkey = hash->function(key);
+ chain = hkey & hash->mask; /* 4 */
+
+ node->key = key;
+ node->hkey = hkey;
+ node->next = hash->table[chain];
+ hash->table[chain] = node;
+ hash->nodecount++;
+
+ assert (hash_verify(hash));
+}
+
+/*
+ * Find a node in the hash table and return a pointer to it.
+ * Notes:
+ * 1. We hash the key and keep the entire hash value. As an optimization, when
+ * we descend down the chain, we can compare hash values first and only if
+ * hash values match do we perform a full key comparison.
+ * 2. To locate the chain from among 2^N chains, we look at the lower N bits of
+ * the hash value by anding them with the current mask.
+ * 3. Looping through the chain, we compare the stored hash value inside each
+ * node against our computed hash. If they match, then we do a full
+ * comparison between the unhashed keys. If these match, we have located the
+ * entry.
+ */
+
+CP_HIDDEN hnode_t *hash_lookup(hash_t *hash, const void *key)
+{
+ hash_val_t hkey, chain;
+ hnode_t *nptr;
+
+ hkey = hash->function(key); /* 1 */
+ chain = hkey & hash->mask; /* 2 */
+
+ for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */
+ if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0)
+ return nptr;
+ }
+
+ return NULL;
+}
+
+/*
+ * Delete the given node from the hash table. Since the chains
+ * are singly linked, we must locate the start of the node's chain
+ * and traverse.
+ * Notes:
+ * 1. The node must belong to this hash table, and its key must not have
+ * been tampered with.
+ * 2. If this deletion will take the node count below the low mark, we
+ * shrink the table now.
+ * 3. Determine which chain the node belongs to, and fetch the pointer
+ * to the first node in this chain.
+ * 4. If the node being deleted is the first node in the chain, then
+ * simply update the chain head pointer.
+ * 5. Otherwise advance to the node's predecessor, and splice out
+ * by updating the predecessor's next pointer.
+ * 6. Indicate that the node is no longer in a hash table.
+ */
+
+CP_HIDDEN hnode_t *hash_delete(hash_t *hash, hnode_t *node)
+{
+ hash_val_t chain;
+ hnode_t *hptr;
+
+ assert (hash_lookup(hash, node->key) == node); /* 1 */
+ assert (hash_val_t_bit != 0);
+
+ if (hash->dynamic && hash->nodecount <= hash->lowmark
+ && hash->nodecount > INIT_SIZE)
+ shrink_table(hash); /* 2 */
+
+ chain = node->hkey & hash->mask; /* 3 */
+ hptr = hash->table[chain];
+
+ if (hptr == node) { /* 4 */
+ hash->table[chain] = node->next;
+ } else {
+ while (hptr->next != node) { /* 5 */
+ assert (hptr != 0);
+ hptr = hptr->next;
+ }
+ assert (hptr->next == node);
+ hptr->next = node->next;
+ }
+
+ hash->nodecount--;
+ assert (hash_verify(hash));
+
+ node->next = NULL; /* 6 */
+ return node;
+}
+
+CP_HIDDEN int hash_alloc_insert(hash_t *hash, const void *key, void *data)
+{
+ hnode_t *node = hash->allocnode(hash->context);
+
+ if (node) {
+ hnode_init(node, data);
+ hash_insert(hash, node, key);
+ return 1;
+ }
+ return 0;
+}
+
+CP_HIDDEN void hash_delete_free(hash_t *hash, hnode_t *node)
+{
+ hash_delete(hash, node);
+ hash->freenode(node, hash->context);
+}
+
+/*
+ * Exactly like hash_delete, except does not trigger table shrinkage. This is to be
+ * used from within a hash table scan operation. See notes for hash_delete.
+ */
+
+CP_HIDDEN hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node)
+{
+ hash_val_t chain;
+ hnode_t *hptr;
+
+ assert (hash_lookup(hash, node->key) == node);
+ assert (hash_val_t_bit != 0);
+
+ chain = node->hkey & hash->mask;
+ hptr = hash->table[chain];
+
+ if (hptr == node) {
+ hash->table[chain] = node->next;
+ } else {
+ while (hptr->next != node)
+ hptr = hptr->next;
+ hptr->next = node->next;
+ }
+
+ hash->nodecount--;
+ assert (hash_verify(hash));
+ node->next = NULL;
+
+ return node;
+}
+
+/*
+ * Like hash_delete_free but based on hash_scan_delete.
+ */
+
+CP_HIDDEN void hash_scan_delfree(hash_t *hash, hnode_t *node)
+{
+ hash_scan_delete(hash, node);
+ hash->freenode(node, hash->context);
+}
+
+/*
+ * Verify whether the given object is a valid hash table. This means
+ * Notes:
+ * 1. If the hash table is dynamic, verify whether the high and
+ * low expansion/shrinkage thresholds are powers of two.
+ * 2. Count all nodes in the table, and test each hash value
+ * to see whether it is correct for the node's chain.
+ */
+
+CP_HIDDEN int hash_verify(hash_t *hash)
+{
+ hashcount_t count = 0;
+ hash_val_t chain;
+ hnode_t *hptr;
+
+ if (hash->dynamic) { /* 1 */
+ if (hash->lowmark >= hash->highmark)
+ return 0;
+ if (!is_power_of_two(hash->highmark))
+ return 0;
+ if (!is_power_of_two(hash->lowmark))
+ return 0;
+ }
+
+ for (chain = 0; chain < hash->nchains; chain++) { /* 2 */
+ for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) {
+ if ((hptr->hkey & hash->mask) != chain)
+ return 0;
+ count++;
+ }
+ }
+
+ if (count != hash->nodecount)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Test whether the hash table is full and return 1 if this is true,
+ * 0 if it is false.
+ */
+
+#undef hash_isfull
+CP_HIDDEN int hash_isfull(hash_t *hash)
+{
+ return hash->nodecount == hash->maxcount;
+}
+
+/*
+ * Test whether the hash table is empty and return 1 if this is true,
+ * 0 if it is false.
+ */
+
+#undef hash_isempty
+CP_HIDDEN int hash_isempty(hash_t *hash)
+{
+ return hash->nodecount == 0;
+}
+
+static hnode_t *hnode_alloc(void *context)
+{
+ return malloc(sizeof *hnode_alloc(NULL));
+}
+
+static void hnode_free(hnode_t *node, void *context)
+{
+ free(node);
+}
+
+
+/*
+ * Create a hash table node dynamically and assign it the given data.
+ */
+
+CP_HIDDEN hnode_t *hnode_create(void *data)
+{
+ hnode_t *node = malloc(sizeof *node);
+ if (node) {
+ node->data = data;
+ node->next = NULL;
+ }
+ return node;
+}
+
+/*
+ * Initialize a client-supplied node
+ */
+
+CP_HIDDEN hnode_t *hnode_init(hnode_t *hnode, void *data)
+{
+ hnode->data = data;
+ hnode->next = NULL;
+ return hnode;
+}
+
+/*
+ * Destroy a dynamically allocated node.
+ */
+
+CP_HIDDEN void hnode_destroy(hnode_t *hnode)
+{
+ free(hnode);
+}
+
+#undef hnode_put
+CP_HIDDEN void hnode_put(hnode_t *node, void *data)
+{
+ node->data = data;
+}
+
+#undef hnode_get
+CP_HIDDEN void *hnode_get(hnode_t *node)
+{
+ return node->data;
+}
+
+#undef hnode_getkey
+CP_HIDDEN const void *hnode_getkey(hnode_t *node)
+{
+ return node->key;
+}
+
+#undef hash_count
+CP_HIDDEN hashcount_t hash_count(hash_t *hash)
+{
+ return hash->nodecount;
+}
+
+#undef hash_size
+CP_HIDDEN hashcount_t hash_size(hash_t *hash)
+{
+ return hash->nchains;
+}
+
+static hash_val_t hash_fun_default(const void *key)
+{
+ static unsigned long randbox[] = {
+ 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
+ 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
+ 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
+ 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
+ };
+
+ const unsigned char *str = key;
+ hash_val_t acc = 0;
+
+ while (*str) {
+ acc ^= randbox[(*str + acc) & 0xf];
+ acc = (acc << 1) | (acc >> 31);
+ acc &= 0xffffffffU;
+ acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
+ acc = (acc << 2) | (acc >> 30);
+ acc &= 0xffffffffU;
+ }
+ return acc;
+}
+
+static int hash_comp_default(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+ char **tokptr;
+ va_list arglist;
+ int tokcount = 0;
+
+ va_start(arglist, string);
+ tokptr = va_arg(arglist, char **);
+ while (tokptr) {
+ while (*string && isspace((unsigned char) *string))
+ string++;
+ if (!*string)
+ break;
+ *tokptr = string;
+ while (*string && !isspace((unsigned char) *string))
+ string++;
+ tokptr = va_arg(arglist, char **);
+ tokcount++;
+ if (!*string)
+ break;
+ *string++ = 0;
+ }
+ va_end(arglist);
+
+ return tokcount;
+}
+
+static char *dupstring(char *str)
+{
+ int sz = strlen(str) + 1;
+ char *new = malloc(sz);
+ if (new)
+ memcpy(new, str, sz);
+ return new;
+}
+
+static hnode_t *new_node(void *c)
+{
+ static hnode_t few[5];
+ static int count;
+
+ if (count < 5)
+ return few + count++;
+
+ return NULL;
+}
+
+static void del_node(hnode_t *n, void *c)
+{
+}
+
+int main(void)
+{
+ input_t in;
+ hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0);
+ hnode_t *hn;
+ hscan_t hs;
+ char *tok1, *tok2, *val;
+ const char *key;
+ int prompt = 0;
+
+ char *help =
+ "a <key> <val> add value to hash table\n"
+ "d <key> delete value from hash table\n"
+ "l <key> lookup value in hash table\n"
+ "n show size of hash table\n"
+ "c show number of entries\n"
+ "t dump whole hash table\n"
+ "+ increase hash table (private func)\n"
+ "- decrease hash table (private func)\n"
+ "b print hash_t_bit value\n"
+ "p turn prompt on\n"
+ "s switch to non-functioning allocator\n"
+ "q quit";
+
+ if (!h)
+ puts("hash_create failed");
+
+ for (;;) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch(in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'b':
+ printf("%d\n", hash_val_t_bit);
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+
+ if (!key || !val) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ }
+
+ if (!hash_alloc_insert(h, key, val)) {
+ puts("hash_alloc_insert failed");
+ free((void *) key);
+ free(val);
+ break;
+ }
+ break;
+ case 'd':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ hn = hash_lookup(h, tok1);
+ if (!hn) {
+ puts("hash_lookup failed");
+ break;
+ }
+ val = hnode_get(hn);
+ key = hnode_getkey(hn);
+ hash_scan_delfree(h, hn);
+ free((void *) key);
+ free(val);
+ break;
+ case 'l':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ hn = hash_lookup(h, tok1);
+ if (!hn) {
+ puts("hash_lookup failed");
+ break;
+ }
+ val = hnode_get(hn);
+ puts(val);
+ break;
+ case 'n':
+ printf("%lu\n", (unsigned long) hash_size(h));
+ break;
+ case 'c':
+ printf("%lu\n", (unsigned long) hash_count(h));
+ break;
+ case 't':
+ hash_scan_begin(&hs, h);
+ while ((hn = hash_scan_next(&hs)))
+ printf("%s\t%s\n", (char*) hnode_getkey(hn),
+ (char*) hnode_get(hn));
+ break;
+ case '+':
+ grow_table(h); /* private function */
+ break;
+ case '-':
+ shrink_table(h); /* private function */
+ break;
+ case 'q':
+ exit(0);
+ break;
+ case '\0':
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 's':
+ hash_set_allocator(h, new_node, del_node, NULL);
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/lib/cpluff/kazlib/hash.h b/lib/cpluff/kazlib/hash.h
new file mode 100644
index 0000000000..1fbd2462c7
--- /dev/null
+++ b/lib/cpluff/kazlib/hash.h
@@ -0,0 +1,248 @@
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include "../libcpluff/cpluffdef.h"
+
+#include <limits.h>
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long hashcount_t;
+#define HASHCOUNT_T_MAX ULONG_MAX
+
+typedef unsigned long hash_val_t;
+#define HASH_VAL_T_MAX ULONG_MAX
+
+CP_HIDDEN extern int hash_val_t_bit;
+
+#ifndef HASH_VAL_T_BIT
+#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
+#endif
+
+/*
+ * Hash chain node structure.
+ * Notes:
+ * 1. This preprocessing directive is for debugging purposes. The effect is
+ * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
+ * inclusion of this header, then the structure shall be declared as having
+ * the single member int __OPAQUE__. This way, any attempts by the
+ * client code to violate the principles of information hiding (by accessing
+ * the structure directly) can be diagnosed at translation time. However,
+ * note the resulting compiled unit is not suitable for linking.
+ * 2. This is a pointer to the next node in the chain. In the last node of a
+ * chain, this pointer is null.
+ * 3. The key is a pointer to some user supplied data that contains a unique
+ * identifier for each hash node in a given table. The interpretation of
+ * the data is up to the user. When creating or initializing a hash table,
+ * the user must supply a pointer to a function for comparing two keys,
+ * and a pointer to a function for hashing a key into a numeric value.
+ * 4. The value is a user-supplied pointer to void which may refer to
+ * any data object. It is not interpreted in any way by the hashing
+ * module.
+ * 5. The hashed key is stored in each node so that we don't have to rehash
+ * each key when the table must grow or shrink.
+ */
+
+typedef struct hnode_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */
+ struct hnode_t *hash_next; /* 2 */
+ const void *hash_key; /* 3 */
+ void *hash_data; /* 4 */
+ hash_val_t hash_hkey; /* 5 */
+ #else
+ int hash_dummy;
+ #endif
+} hnode_t;
+
+/*
+ * The comparison function pointer type. A comparison function takes two keys
+ * and produces a value of -1 if the left key is less than the right key, a
+ * value of 0 if the keys are equal, and a value of 1 if the left key is
+ * greater than the right key.
+ */
+
+typedef int (*hash_comp_t)(const void *, const void *);
+
+/*
+ * The hashing function performs some computation on a key and produces an
+ * integral value of type hash_val_t based on that key. For best results, the
+ * function should have a good randomness properties in *all* significant bits
+ * over the set of keys that are being inserted into a given hash table. In
+ * particular, the most significant bits of hash_val_t are most significant to
+ * the hash module. Only as the hash table expands are less significant bits
+ * examined. Thus a function that has good distribution in its upper bits but
+ * not lower is preferrable to one that has poor distribution in the upper bits
+ * but not the lower ones.
+ */
+
+typedef hash_val_t (*hash_fun_t)(const void *);
+
+/*
+ * allocator functions
+ */
+
+typedef hnode_t *(*hnode_alloc_t)(void *);
+typedef void (*hnode_free_t)(hnode_t *, void *);
+
+/*
+ * This is the hash table control structure. It keeps track of information
+ * about a hash table, as well as the hash table itself.
+ * Notes:
+ * 1. Pointer to the hash table proper. The table is an array of pointers to
+ * hash nodes (of type hnode_t). If the table is empty, every element of
+ * this table is a null pointer. A non-null entry points to the first
+ * element of a chain of nodes.
+ * 2. This member keeps track of the size of the hash table---that is, the
+ * number of chain pointers.
+ * 3. The count member maintains the number of elements that are presently
+ * in the hash table.
+ * 4. The maximum count is the greatest number of nodes that can populate this
+ * table. If the table contains this many nodes, no more can be inserted,
+ * and the hash_isfull() function returns true.
+ * 5. The high mark is a population threshold, measured as a number of nodes,
+ * which, if exceeded, will trigger a table expansion. Only dynamic hash
+ * tables are subject to this expansion.
+ * 6. The low mark is a minimum population threshold, measured as a number of
+ * nodes. If the table population drops below this value, a table shrinkage
+ * will occur. Only dynamic tables are subject to this reduction. No table
+ * will shrink beneath a certain absolute minimum number of nodes.
+ * 7. This is the a pointer to the hash table's comparison function. The
+ * function is set once at initialization or creation time.
+ * 8. Pointer to the table's hashing function, set once at creation or
+ * initialization time.
+ * 9. The current hash table mask. If the size of the hash table is 2^N,
+ * this value has its low N bits set to 1, and the others clear. It is used
+ * to select bits from the result of the hashing function to compute an
+ * index into the table.
+ * 10. A flag which indicates whether the table is to be dynamically resized. It
+ * is set to 1 in dynamically allocated tables, 0 in tables that are
+ * statically allocated.
+ */
+
+typedef struct hash_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct hnode_t **hash_table; /* 1 */
+ hashcount_t hash_nchains; /* 2 */
+ hashcount_t hash_nodecount; /* 3 */
+ hashcount_t hash_maxcount; /* 4 */
+ hashcount_t hash_highmark; /* 5 */
+ hashcount_t hash_lowmark; /* 6 */
+ hash_comp_t hash_compare; /* 7 */
+ hash_fun_t hash_function; /* 8 */
+ hnode_alloc_t hash_allocnode;
+ hnode_free_t hash_freenode;
+ void *hash_context;
+ hash_val_t hash_mask; /* 9 */
+ int hash_dynamic; /* 10 */
+ #else
+ int hash_dummy;
+ #endif
+} hash_t;
+
+/*
+ * Hash scanner structure, used for traversals of the data structure.
+ * Notes:
+ * 1. Pointer to the hash table that is being traversed.
+ * 2. Reference to the current chain in the table being traversed (the chain
+ * that contains the next node that shall be retrieved).
+ * 3. Pointer to the node that will be retrieved by the subsequent call to
+ * hash_scan_next().
+ */
+
+typedef struct hscan_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ hash_t *hash_table; /* 1 */
+ hash_val_t hash_chain; /* 2 */
+ hnode_t *hash_next; /* 3 */
+ #else
+ int hash_dummy;
+ #endif
+} hscan_t;
+
+CP_HIDDEN extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
+CP_HIDDEN extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
+CP_HIDDEN extern void hash_destroy(hash_t *);
+CP_HIDDEN extern void hash_free_nodes(hash_t *);
+CP_HIDDEN extern void hash_free(hash_t *);
+CP_HIDDEN extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
+ hash_fun_t, hnode_t **, hashcount_t);
+CP_HIDDEN extern void hash_insert(hash_t *, hnode_t *, const void *);
+CP_HIDDEN extern hnode_t *hash_lookup(hash_t *, const void *);
+CP_HIDDEN extern hnode_t *hash_delete(hash_t *, hnode_t *);
+CP_HIDDEN extern int hash_alloc_insert(hash_t *, const void *, void *);
+CP_HIDDEN extern void hash_delete_free(hash_t *, hnode_t *);
+
+CP_HIDDEN extern void hnode_put(hnode_t *, void *);
+CP_HIDDEN extern void *hnode_get(hnode_t *);
+CP_HIDDEN extern const void *hnode_getkey(hnode_t *);
+CP_HIDDEN extern hashcount_t hash_count(hash_t *);
+CP_HIDDEN extern hashcount_t hash_size(hash_t *);
+
+CP_HIDDEN extern int hash_isfull(hash_t *);
+CP_HIDDEN extern int hash_isempty(hash_t *);
+
+CP_HIDDEN extern void hash_scan_begin(hscan_t *, hash_t *);
+CP_HIDDEN extern hnode_t *hash_scan_next(hscan_t *);
+CP_HIDDEN extern hnode_t *hash_scan_delete(hash_t *, hnode_t *);
+CP_HIDDEN extern void hash_scan_delfree(hash_t *, hnode_t *);
+
+CP_HIDDEN extern int hash_verify(hash_t *);
+
+CP_HIDDEN extern hnode_t *hnode_create(void *);
+CP_HIDDEN extern hnode_t *hnode_init(hnode_t *, void *);
+CP_HIDDEN extern void hnode_destroy(hnode_t *);
+
+#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount)
+#else
+#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount)
+#endif
+#define hash_isempty(H) ((H)->hash_nodecount == 0)
+#define hash_count(H) ((H)->hash_nodecount)
+#define hash_size(H) ((H)->hash_nchains)
+#define hnode_get(N) ((N)->hash_data)
+#define hnode_getkey(N) ((N)->hash_key)
+#define hnode_put(N, V) ((N)->hash_data = (V))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/cpluff/kazlib/list.c b/lib/cpluff/kazlib/list.c
new file mode 100644
index 0000000000..dc8eed0e63
--- /dev/null
+++ b/lib/cpluff/kazlib/list.c
@@ -0,0 +1,935 @@
+/*
+ * List Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#define LIST_IMPLEMENTATION
+#include "list.h"
+
+#define next list_next
+#define prev list_prev
+#define data list_data
+
+#define pool list_pool
+#define fre list_free
+#define size list_size
+
+#define nilnode list_nilnode
+#define nodecount list_nodecount
+#define maxcount list_maxcount
+
+#define list_nil(L) (&(L)->nilnode)
+#define list_first_priv(L) ((L)->nilnode.next)
+#define list_last_priv(L) ((L)->nilnode.prev)
+#define lnode_next(N) ((N)->next)
+#define lnode_prev(N) ((N)->prev)
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $";
+#endif
+
+/*
+ * Initialize a list object supplied by the client such that it becomes a valid
+ * empty list. If the list is to be ``unbounded'', the maxcount should be
+ * specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero
+ * is not permitted.
+ */
+
+CP_HIDDEN list_t *list_init(list_t *list, listcount_t maxcount)
+{
+ assert (maxcount != 0);
+ list->nilnode.next = &list->nilnode;
+ list->nilnode.prev = &list->nilnode;
+ list->nodecount = 0;
+ list->maxcount = maxcount;
+ return list;
+}
+
+/*
+ * Dynamically allocate a list object using malloc(), and initialize it so that
+ * it is a valid empty list. If the list is to be ``unbounded'', the maxcount
+ * should be specified as LISTCOUNT_T_MAX, or, alternately, as -1.
+ */
+
+CP_HIDDEN list_t *list_create(listcount_t maxcount)
+{
+ list_t *new = malloc(sizeof *new);
+ if (new) {
+ assert (maxcount != 0);
+ new->nilnode.next = &new->nilnode;
+ new->nilnode.prev = &new->nilnode;
+ new->nodecount = 0;
+ new->maxcount = maxcount;
+ }
+ return new;
+}
+
+/*
+ * Destroy a dynamically allocated list object.
+ * The client must remove the nodes first.
+ */
+
+CP_HIDDEN void list_destroy(list_t *list)
+{
+ assert (list_isempty(list));
+ free(list);
+}
+
+/*
+ * Free all of the nodes of a list. The list must contain only
+ * dynamically allocated nodes. After this call, the list
+ * is empty.
+ */
+
+CP_HIDDEN void list_destroy_nodes(list_t *list)
+{
+ lnode_t *lnode = list_first_priv(list), *nil = list_nil(list), *tmp;
+
+ while (lnode != nil) {
+ tmp = lnode->next;
+ lnode->next = NULL;
+ lnode->prev = NULL;
+ lnode_destroy(lnode);
+ lnode = tmp;
+ }
+
+ list_init(list, list->maxcount);
+}
+
+/*
+ * Return all of the nodes of a list to a node pool. The nodes in
+ * the list must all have come from the same pool.
+ */
+
+CP_HIDDEN void list_return_nodes(list_t *list, lnodepool_t *pool)
+{
+ lnode_t *lnode = list_first_priv(list), *tmp, *nil = list_nil(list);
+
+ while (lnode != nil) {
+ tmp = lnode->next;
+ lnode->next = NULL;
+ lnode->prev = NULL;
+ lnode_return(pool, lnode);
+ lnode = tmp;
+ }
+
+ list_init(list, list->maxcount);
+}
+
+/*
+ * Insert the node ``new'' into the list immediately after ``this'' node.
+ */
+
+CP_HIDDEN void list_ins_after(list_t *list, lnode_t *new, lnode_t *this)
+{
+ lnode_t *that = this->next;
+
+ assert (new != NULL);
+ assert (!list_contains(list, new));
+ assert (!lnode_is_in_a_list(new));
+ assert (this == list_nil(list) || list_contains(list, this));
+ assert (list->nodecount + 1 > list->nodecount);
+
+ new->prev = this;
+ new->next = that;
+ that->prev = new;
+ this->next = new;
+ list->nodecount++;
+
+ assert (list->nodecount <= list->maxcount);
+}
+
+/*
+ * Insert the node ``new'' into the list immediately before ``this'' node.
+ */
+
+CP_HIDDEN void list_ins_before(list_t *list, lnode_t *new, lnode_t *this)
+{
+ lnode_t *that = this->prev;
+
+ assert (new != NULL);
+ assert (!list_contains(list, new));
+ assert (!lnode_is_in_a_list(new));
+ assert (this == list_nil(list) || list_contains(list, this));
+ assert (list->nodecount + 1 > list->nodecount);
+
+ new->next = this;
+ new->prev = that;
+ that->next = new;
+ this->prev = new;
+ list->nodecount++;
+
+ assert (list->nodecount <= list->maxcount);
+}
+
+/*
+ * Delete the given node from the list.
+ */
+
+CP_HIDDEN lnode_t *list_delete(list_t *list, lnode_t *del)
+{
+ lnode_t *next = del->next;
+ lnode_t *prev = del->prev;
+
+ assert (list_contains(list, del));
+
+ prev->next = next;
+ next->prev = prev;
+ list->nodecount--;
+
+ del->next = del->prev = NULL;
+
+ return del;
+}
+
+/*
+ * For each node in the list, execute the given function. The list,
+ * current node and the given context pointer are passed on each
+ * call to the function.
+ */
+
+CP_HIDDEN void list_process(list_t *list, void *context,
+ void (* function)(list_t *list, lnode_t *lnode, void *context))
+{
+ lnode_t *node = list_first_priv(list), *next, *nil = list_nil(list);
+
+ while (node != nil) {
+ /* check for callback function deleting */
+ /* the next node from under us */
+ assert (list_contains(list, node));
+ next = node->next;
+ function(list, node, context);
+ node = next;
+ }
+}
+
+/*
+ * Dynamically allocate a list node and assign it the given piece of data.
+ */
+
+CP_HIDDEN lnode_t *lnode_create(void *data)
+{
+ lnode_t *new = malloc(sizeof *new);
+ if (new) {
+ new->data = data;
+ new->next = NULL;
+ new->prev = NULL;
+ }
+ return new;
+}
+
+/*
+ * Initialize a user-supplied lnode.
+ */
+
+CP_HIDDEN lnode_t *lnode_init(lnode_t *lnode, void *data)
+{
+ lnode->data = data;
+ lnode->next = NULL;
+ lnode->prev = NULL;
+ return lnode;
+}
+
+/*
+ * Destroy a dynamically allocated node.
+ */
+
+CP_HIDDEN void lnode_destroy(lnode_t *lnode)
+{
+ assert (!lnode_is_in_a_list(lnode));
+ free(lnode);
+}
+
+/*
+ * Initialize a node pool object to use a user-supplied set of nodes.
+ * The ``nodes'' pointer refers to an array of lnode_t objects, containing
+ * ``n'' elements.
+ */
+
+CP_HIDDEN lnodepool_t *lnode_pool_init(lnodepool_t *pool, lnode_t *nodes, listcount_t n)
+{
+ listcount_t i;
+
+ assert (n != 0);
+
+ pool->pool = nodes;
+ pool->fre = nodes;
+ pool->size = n;
+ for (i = 0; i < n - 1; i++) {
+ nodes[i].next = nodes + i + 1;
+ }
+ nodes[i].next = NULL;
+ nodes[i].prev = nodes; /* to make sure node is marked ``on list'' */
+ return pool;
+}
+
+/*
+ * Create a dynamically allocated pool of n nodes.
+ */
+
+CP_HIDDEN lnodepool_t *lnode_pool_create(listcount_t n)
+{
+ lnodepool_t *pool;
+ lnode_t *nodes;
+
+ assert (n != 0);
+
+ pool = malloc(sizeof *pool);
+ if (!pool)
+ return NULL;
+ nodes = malloc(n * sizeof *nodes);
+ if (!nodes) {
+ free(pool);
+ return NULL;
+ }
+ lnode_pool_init(pool, nodes, n);
+ return pool;
+}
+
+/*
+ * Determine whether the given pool is from this pool.
+ */
+
+CP_HIDDEN int lnode_pool_isfrom(lnodepool_t *pool, lnode_t *node)
+{
+ listcount_t i;
+
+ /* this is carefully coded this way because ANSI C forbids pointers
+ to different objects from being subtracted or compared other
+ than for exact equality */
+
+ for (i = 0; i < pool->size; i++) {
+ if (pool->pool + i == node)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Destroy a dynamically allocated pool of nodes.
+ */
+
+CP_HIDDEN void lnode_pool_destroy(lnodepool_t *p)
+{
+ free(p->pool);
+ free(p);
+}
+
+/*
+ * Borrow a node from a node pool. Returns a null pointer if the pool
+ * is exhausted.
+ */
+
+CP_HIDDEN lnode_t *lnode_borrow(lnodepool_t *pool, void *data)
+{
+ lnode_t *new = pool->fre;
+ if (new) {
+ pool->fre = new->next;
+ new->data = data;
+ new->next = NULL;
+ new->prev = NULL;
+ }
+ return new;
+}
+
+/*
+ * Return a node to a node pool. A node must be returned to the pool
+ * from which it came.
+ */
+
+CP_HIDDEN void lnode_return(lnodepool_t *pool, lnode_t *node)
+{
+ assert (lnode_pool_isfrom(pool, node));
+ assert (!lnode_is_in_a_list(node));
+
+ node->next = pool->fre;
+ node->prev = node;
+ pool->fre = node;
+}
+
+/*
+ * Determine whether the given list contains the given node.
+ * According to this function, a list does not contain its nilnode.
+ */
+
+CP_HIDDEN int list_contains(list_t *list, lnode_t *node)
+{
+ lnode_t *n, *nil = list_nil(list);
+
+ for (n = list_first_priv(list); n != nil; n = lnode_next(n)) {
+ if (node == n)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * A more generalized variant of list_transfer. This one removes a
+ * ``slice'' from the source list and appends it to the destination
+ * list.
+ */
+
+CP_HIDDEN void list_extract(list_t *dest, list_t *source, lnode_t *first, lnode_t *last)
+{
+ listcount_t moved = 1;
+
+ assert (first == NULL || list_contains(source, first));
+ assert (last == NULL || list_contains(source, last));
+
+ if (first == NULL || last == NULL)
+ return;
+
+ /* adjust the destination list so that the slice is spliced out */
+
+ first->prev->next = last->next;
+ last->next->prev = first->prev;
+
+ /* graft the splice at the end of the dest list */
+
+ last->next = &dest->nilnode;
+ first->prev = dest->nilnode.prev;
+ dest->nilnode.prev->next = first;
+ dest->nilnode.prev = last;
+
+ while (first != last) {
+ first = first->next;
+ assert (first != list_nil(source)); /* oops, last before first! */
+ moved++;
+ }
+
+ /* assert no overflows */
+ assert (source->nodecount - moved <= source->nodecount);
+ assert (dest->nodecount + moved >= dest->nodecount);
+
+ /* assert no weirdness */
+ assert (moved <= source->nodecount);
+
+ source->nodecount -= moved;
+ dest->nodecount += moved;
+
+ /* assert list sanity */
+ assert (list_verify(source));
+ assert (list_verify(dest));
+}
+
+
+/*
+ * Split off a trailing sequence of nodes from the source list and relocate
+ * them to the tail of the destination list. The trailing sequence begins
+ * with node ``first'' and terminates with the last node of the source
+ * list. The nodes are added to the end of the new list in their original
+ * order.
+ */
+
+CP_HIDDEN void list_transfer(list_t *dest, list_t *source, lnode_t *first)
+{
+ listcount_t moved = 1;
+ lnode_t *last;
+
+ assert (first == NULL || list_contains(source, first));
+
+ if (first == NULL)
+ return;
+
+ last = source->nilnode.prev;
+
+ source->nilnode.prev = first->prev;
+ first->prev->next = &source->nilnode;
+
+ last->next = &dest->nilnode;
+ first->prev = dest->nilnode.prev;
+ dest->nilnode.prev->next = first;
+ dest->nilnode.prev = last;
+
+ while (first != last) {
+ first = first->next;
+ moved++;
+ }
+
+ /* assert no overflows */
+ assert (source->nodecount - moved <= source->nodecount);
+ assert (dest->nodecount + moved >= dest->nodecount);
+
+ /* assert no weirdness */
+ assert (moved <= source->nodecount);
+
+ source->nodecount -= moved;
+ dest->nodecount += moved;
+
+ /* assert list sanity */
+ assert (list_verify(source));
+ assert (list_verify(dest));
+}
+
+CP_HIDDEN void list_merge(list_t *dest, list_t *sour,
+ int compare (const void *, const void *))
+{
+ lnode_t *dn, *sn, *tn;
+ lnode_t *d_nil = list_nil(dest), *s_nil = list_nil(sour);
+
+ /* Nothing to do if source and destination list are the same. */
+ if (dest == sour)
+ return;
+
+ /* overflow check */
+ assert (list_count(sour) + list_count(dest) >= list_count(sour));
+
+ /* lists must be sorted */
+ assert (list_is_sorted(sour, compare));
+ assert (list_is_sorted(dest, compare));
+
+ dn = list_first_priv(dest);
+ sn = list_first_priv(sour);
+
+ while (dn != d_nil && sn != s_nil) {
+ if (compare(lnode_get(dn), lnode_get(sn)) >= 0) {
+ tn = lnode_next(sn);
+ list_delete(sour, sn);
+ list_ins_before(dest, sn, dn);
+ sn = tn;
+ } else {
+ dn = lnode_next(dn);
+ }
+ }
+
+ if (dn != d_nil)
+ return;
+
+ if (sn != s_nil)
+ list_transfer(dest, sour, sn);
+}
+
+CP_HIDDEN void list_sort(list_t *list, int compare(const void *, const void *))
+{
+ list_t extra;
+ listcount_t middle;
+ lnode_t *node;
+
+ if (list_count(list) > 1) {
+ middle = list_count(list) / 2;
+ node = list_first_priv(list);
+
+ list_init(&extra, list_count(list) - middle);
+
+ while (middle--)
+ node = lnode_next(node);
+
+ list_transfer(&extra, list, node);
+ list_sort(list, compare);
+ list_sort(&extra, compare);
+ list_merge(list, &extra, compare);
+ }
+ assert (list_is_sorted(list, compare));
+}
+
+CP_HIDDEN lnode_t *list_find(list_t *list, const void *key, int compare(const void *, const void *))
+{
+ lnode_t *node;
+
+ for (node = list_first_priv(list); node != list_nil(list); node = node->next) {
+ if (compare(lnode_get(node), key) == 0)
+ return node;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Return 1 if the list is in sorted order, 0 otherwise
+ */
+
+CP_HIDDEN int list_is_sorted(list_t *list, int compare(const void *, const void *))
+{
+ lnode_t *node, *next, *nil;
+
+ next = nil = list_nil(list);
+ node = list_first_priv(list);
+
+ if (node != nil)
+ next = lnode_next(node);
+
+ for (; next != nil; node = next, next = lnode_next(next)) {
+ if (compare(lnode_get(node), lnode_get(next)) > 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Get rid of macro functions definitions so they don't interfere
+ * with the actual definitions
+ */
+
+#undef list_isempty
+#undef list_isfull
+#undef lnode_pool_isempty
+#undef list_append
+#undef list_prepend
+#undef list_first
+#undef list_last
+#undef list_next
+#undef list_prev
+#undef list_count
+#undef list_del_first
+#undef list_del_last
+#undef lnode_put
+#undef lnode_get
+
+/*
+ * Return 1 if the list is empty, 0 otherwise
+ */
+
+CP_HIDDEN int list_isempty(list_t *list)
+{
+ return list->nodecount == 0;
+}
+
+/*
+ * Return 1 if the list is full, 0 otherwise
+ * Permitted only on bounded lists.
+ */
+
+CP_HIDDEN int list_isfull(list_t *list)
+{
+ return list->nodecount == list->maxcount;
+}
+
+/*
+ * Check if the node pool is empty.
+ */
+
+CP_HIDDEN int lnode_pool_isempty(lnodepool_t *pool)
+{
+ return (pool->fre == NULL);
+}
+
+/*
+ * Add the given node at the end of the list
+ */
+
+CP_HIDDEN void list_append(list_t *list, lnode_t *node)
+{
+ list_ins_before(list, node, &list->nilnode);
+}
+
+/*
+ * Add the given node at the beginning of the list.
+ */
+
+CP_HIDDEN void list_prepend(list_t *list, lnode_t *node)
+{
+ list_ins_after(list, node, &list->nilnode);
+}
+
+/*
+ * Retrieve the first node of the list
+ */
+
+CP_HIDDEN lnode_t *list_first(list_t *list)
+{
+ if (list->nilnode.next == &list->nilnode)
+ return NULL;
+ return list->nilnode.next;
+}
+
+/*
+ * Retrieve the last node of the list
+ */
+
+CP_HIDDEN lnode_t *list_last(list_t *list)
+{
+ if (list->nilnode.prev == &list->nilnode)
+ return NULL;
+ return list->nilnode.prev;
+}
+
+/*
+ * Retrieve the count of nodes in the list
+ */
+
+CP_HIDDEN listcount_t list_count(list_t *list)
+{
+ return list->nodecount;
+}
+
+/*
+ * Remove the first node from the list and return it.
+ */
+
+CP_HIDDEN lnode_t *list_del_first(list_t *list)
+{
+ return list_delete(list, list->nilnode.next);
+}
+
+/*
+ * Remove the last node from the list and return it.
+ */
+
+CP_HIDDEN lnode_t *list_del_last(list_t *list)
+{
+ return list_delete(list, list->nilnode.prev);
+}
+
+
+/*
+ * Associate a data item with the given node.
+ */
+
+CP_HIDDEN void lnode_put(lnode_t *lnode, void *data)
+{
+ lnode->data = data;
+}
+
+/*
+ * Retrieve the data item associated with the node.
+ */
+
+CP_HIDDEN void *lnode_get(lnode_t *lnode)
+{
+ return lnode->data;
+}
+
+/*
+ * Retrieve the node's successor. If there is no successor,
+ * NULL is returned.
+ */
+
+CP_HIDDEN lnode_t *list_next(list_t *list, lnode_t *lnode)
+{
+ assert (list_contains(list, lnode));
+
+ if (lnode->next == list_nil(list))
+ return NULL;
+ return lnode->next;
+}
+
+/*
+ * Retrieve the node's predecessor. See comment for lnode_next().
+ */
+
+CP_HIDDEN lnode_t *list_prev(list_t *list, lnode_t *lnode)
+{
+ assert (list_contains(list, lnode));
+
+ if (lnode->prev == list_nil(list))
+ return NULL;
+ return lnode->prev;
+}
+
+/*
+ * Return 1 if the lnode is in some list, otherwise return 0.
+ */
+
+CP_HIDDEN int lnode_is_in_a_list(lnode_t *lnode)
+{
+ return (lnode->next != NULL || lnode->prev != NULL);
+}
+
+
+CP_HIDDEN int list_verify(list_t *list)
+{
+ lnode_t *node = list_first_priv(list), *nil = list_nil(list);
+ listcount_t count = list_count(list);
+
+ if (node->prev != nil)
+ return 0;
+
+ if (count > list->maxcount)
+ return 0;
+
+ while (node != nil && count--) {
+ if (node->next->prev != node)
+ return 0;
+ node = node->next;
+ }
+
+ if (count != 0 || node != nil)
+ return 0;
+
+ return 1;
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+ char **tokptr;
+ va_list arglist;
+ int tokcount = 0;
+
+ va_start(arglist, string);
+ tokptr = va_arg(arglist, char **);
+ while (tokptr) {
+ while (*string && isspace((unsigned char) *string))
+ string++;
+ if (!*string)
+ break;
+ *tokptr = string;
+ while (*string && !isspace((unsigned char) *string))
+ string++;
+ tokptr = va_arg(arglist, char **);
+ tokcount++;
+ if (!*string)
+ break;
+ *string++ = 0;
+ }
+ va_end(arglist);
+
+ return tokcount;
+}
+
+static int comparef(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+static char *dupstring(char *str)
+{
+ int sz = strlen(str) + 1;
+ char *new = malloc(sz);
+ if (new)
+ memcpy(new, str, sz);
+ return new;
+}
+
+int main(void)
+{
+ input_t in;
+ list_t *l = list_create(LISTCOUNT_T_MAX);
+ lnode_t *ln;
+ char *tok1, *val;
+ int prompt = 0;
+
+ char *help =
+ "a <val> append value to list\n"
+ "d <val> delete value from list\n"
+ "l <val> lookup value in list\n"
+ "s sort list\n"
+ "c show number of entries\n"
+ "t dump whole list\n"
+ "p turn prompt on\n"
+ "q quit";
+
+ if (!l)
+ puts("list_create failed");
+
+ for (;;) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch(in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ val = dupstring(tok1);
+ ln = lnode_create(val);
+
+ if (!val || !ln) {
+ puts("allocation failure");
+ if (ln)
+ lnode_destroy(ln);
+ free(val);
+ break;
+ }
+
+ list_append(l, ln);
+ break;
+ case 'd':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ ln = list_find(l, tok1, comparef);
+ if (!ln) {
+ puts("list_find failed");
+ break;
+ }
+ list_delete(l, ln);
+ val = lnode_get(ln);
+ lnode_destroy(ln);
+ free(val);
+ break;
+ case 'l':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ ln = list_find(l, tok1, comparef);
+ if (!ln)
+ puts("list_find failed");
+ else
+ puts("found");
+ break;
+ case 's':
+ list_sort(l, comparef);
+ break;
+ case 'c':
+ printf("%lu\n", (unsigned long) list_count(l));
+ break;
+ case 't':
+ for (ln = list_first(l); ln != 0; ln = list_next(l, ln))
+ puts(lnode_get(ln));
+ break;
+ case 'q':
+ exit(0);
+ break;
+ case '\0':
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* defined TEST_MAIN */
diff --git a/lib/cpluff/kazlib/list.h b/lib/cpluff/kazlib/list.h
new file mode 100644
index 0000000000..c449acbb54
--- /dev/null
+++ b/lib/cpluff/kazlib/list.h
@@ -0,0 +1,162 @@
+/*
+ * List Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: list.h,v 1.19 1999/11/14 20:46:19 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#include "../libcpluff/cpluffdef.h"
+
+#include <limits.h>
+
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#define LIST_SFX_CHECK(E) SFX_CHECK(E)
+#else
+#define LIST_SFX_CHECK(E) (E)
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long listcount_t;
+#define LISTCOUNT_T_MAX ULONG_MAX
+
+typedef struct lnode_t {
+ #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct lnode_t *list_next;
+ struct lnode_t *list_prev;
+ void *list_data;
+ #else
+ int list_dummy;
+ #endif
+} lnode_t;
+
+typedef struct lnodepool_t {
+ #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct lnode_t *list_pool;
+ struct lnode_t *list_free;
+ listcount_t list_size;
+ #else
+ int list_dummy;
+ #endif
+} lnodepool_t;
+
+typedef struct list_t {
+ #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ lnode_t list_nilnode;
+ listcount_t list_nodecount;
+ listcount_t list_maxcount;
+ #else
+ int list_dummy;
+ #endif
+} list_t;
+
+CP_HIDDEN lnode_t *lnode_create(void *);
+CP_HIDDEN lnode_t *lnode_init(lnode_t *, void *);
+CP_HIDDEN void lnode_destroy(lnode_t *);
+CP_HIDDEN void lnode_put(lnode_t *, void *);
+CP_HIDDEN void *lnode_get(lnode_t *);
+CP_HIDDEN int lnode_is_in_a_list(lnode_t *);
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define lnode_put(N, D) ((N)->list_data = (D))
+#define lnode_get(N) ((N)->list_data)
+#endif
+
+CP_HIDDEN lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t);
+CP_HIDDEN lnodepool_t *lnode_pool_create(listcount_t);
+CP_HIDDEN void lnode_pool_destroy(lnodepool_t *);
+CP_HIDDEN lnode_t *lnode_borrow(lnodepool_t *, void *);
+CP_HIDDEN void lnode_return(lnodepool_t *, lnode_t *);
+CP_HIDDEN int lnode_pool_isempty(lnodepool_t *);
+CP_HIDDEN int lnode_pool_isfrom(lnodepool_t *, lnode_t *);
+
+CP_HIDDEN list_t *list_init(list_t *, listcount_t);
+CP_HIDDEN list_t *list_create(listcount_t);
+CP_HIDDEN void list_destroy(list_t *);
+CP_HIDDEN void list_destroy_nodes(list_t *);
+CP_HIDDEN void list_return_nodes(list_t *, lnodepool_t *);
+
+CP_HIDDEN listcount_t list_count(list_t *);
+CP_HIDDEN int list_isempty(list_t *);
+CP_HIDDEN int list_isfull(list_t *);
+CP_HIDDEN int list_contains(list_t *, lnode_t *);
+
+CP_HIDDEN void list_append(list_t *, lnode_t *);
+CP_HIDDEN void list_prepend(list_t *, lnode_t *);
+CP_HIDDEN void list_ins_before(list_t *, lnode_t *, lnode_t *);
+CP_HIDDEN void list_ins_after(list_t *, lnode_t *, lnode_t *);
+
+CP_HIDDEN lnode_t *list_first(list_t *);
+CP_HIDDEN lnode_t *list_last(list_t *);
+CP_HIDDEN lnode_t *list_next(list_t *, lnode_t *);
+CP_HIDDEN lnode_t *list_prev(list_t *, lnode_t *);
+
+CP_HIDDEN lnode_t *list_del_first(list_t *);
+CP_HIDDEN lnode_t *list_del_last(list_t *);
+CP_HIDDEN lnode_t *list_delete(list_t *, lnode_t *);
+
+CP_HIDDEN void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *));
+
+CP_HIDDEN int list_verify(list_t *);
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define lnode_pool_isempty(P) ((P)->list_free == 0)
+#define list_count(L) ((L)->list_nodecount)
+#define list_isempty(L) ((L)->list_nodecount == 0)
+#define list_isfull(L) (LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount)
+#define list_next(L, N) (LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next)
+#define list_prev(L, N) (LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev)
+#define list_first(L) list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode)
+#define list_last(L) list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode)
+#endif
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define list_append(L, N) list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
+#define list_prepend(L, N) list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
+#define list_del_first(L) list_delete(LIST_SFX_CHECK(L), list_first(L))
+#define list_del_last(L) list_delete(LIST_SFX_CHECK(L), list_last(L))
+#endif
+
+/* destination list on the left, source on the right */
+
+CP_HIDDEN void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);
+CP_HIDDEN void list_transfer(list_t *, list_t *, lnode_t *first);
+CP_HIDDEN void list_merge(list_t *, list_t *, int (const void *, const void *));
+CP_HIDDEN void list_sort(list_t *, int (const void *, const void *));
+CP_HIDDEN lnode_t *list_find(list_t *, const void *, int (const void *, const void *));
+CP_HIDDEN int list_is_sorted(list_t *, int (const void *, const void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif