diff options
author | alcoheca <alcoheca@svn> | 2010-03-26 13:35:35 +0000 |
---|---|---|
committer | alcoheca <alcoheca@svn> | 2010-03-26 13:35:35 +0000 |
commit | b632a42d5cacdda7bb07a3fee995a4f0de1deba9 (patch) | |
tree | b174ecd7ce4830785293fa6d18ab65d30c0cc375 /lib/cpluff/kazlib | |
parent | 5ccef76d00e1dc22d6ae5d1c08172630f5972e58 (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.c | 1041 | ||||
-rw-r--r-- | lib/cpluff/kazlib/hash.h | 248 | ||||
-rw-r--r-- | lib/cpluff/kazlib/list.c | 935 | ||||
-rw-r--r-- | lib/cpluff/kazlib/list.h | 162 |
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 |