/* NeoStats - IRC Statistical Services ** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond ** http://www.neostats.net/ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ** USA ** ** Portions borrowed from kazlib. Header follows: */ /* * List Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * 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$ * $Name: $ */ #include #include #include #include #define LIST_IMPLEMENTATION #include "list.h" #include "log.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$"; #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. */ list_t * list_init (list_t * list, listcount_t maxcount) { nassert (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. */ list_t * list_create (listcount_t maxcount) { list_t *new = malloc (sizeof *new); if (new) { nassert (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. */ void list_destroy (list_t * list) { nassert (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. */ 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. */ 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. */ void list_ins_after (list_t * list, lnode_t * new, lnode_t * this) { lnode_t *that = this->next; nassert (new != NULL); nassert (!list_contains (list, new)); nassert (!lnode_is_in_a_list (new)); nassert (this == list_nil (list) || list_contains (list, this)); nassert (list->nodecount + 1 > list->nodecount); new->prev = this; new->next = that; that->prev = new; this->next = new; list->nodecount++; nassert (list->nodecount <= list->maxcount); } /* * Insert the node ``new'' into the list immediately before ``this'' node. */ void list_ins_before (list_t * list, lnode_t * new, lnode_t * this) { lnode_t *that = this->prev; nassert (new != NULL); nassert (!list_contains (list, new)); nassert (!lnode_is_in_a_list (new)); nassert (this == list_nil (list) || list_contains (list, this)); nassert (list->nodecount + 1 > list->nodecount); new->next = this; new->prev = that; that->next = new; this->prev = new; list->nodecount++; nassert (list->nodecount <= list->maxcount); } /* * Delete the given node from the list. */ lnode_t * list_delete (list_t * list, lnode_t * del) { lnode_t *next = del->next; lnode_t *prev = del->prev; nassert (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. */ 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 */ nassert (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. */ 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. */ 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. */ void lnode_destroy (lnode_t * lnode) { nassert (!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. */ lnodepool_t * lnode_pool_init (lnodepool_t * pool, lnode_t * nodes, listcount_t n) { listcount_t i; nassert (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. */ lnodepool_t * lnode_pool_create (listcount_t n) { lnodepool_t *pool; lnode_t *nodes; nassert (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. */ 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. */ 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. */ 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. */ void lnode_return (lnodepool_t * pool, lnode_t * node) { nassert (lnode_pool_isfrom (pool, node)); nassert (!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. */ 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. */ void list_extract (list_t * dest, list_t * source, lnode_t * first, lnode_t * last) { listcount_t moved = 1; nassert (first == NULL || list_contains (source, first)); nassert (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; nassert (first != list_nil (source)); /* oops, last before first! */ moved++; } /* nassert no overflows */ nassert (source->nodecount - moved <= source->nodecount); nassert (dest->nodecount + moved >= dest->nodecount); /* nassert no weirdness */ nassert (moved <= source->nodecount); source->nodecount -= moved; dest->nodecount += moved; /* nassert list sanity */ nassert (list_verify (source)); nassert (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. */ void list_transfer (list_t * dest, list_t * source, lnode_t * first) { listcount_t moved = 1; lnode_t *last; nassert (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++; } /* nassert no overflows */ nassert (source->nodecount - moved <= source->nodecount); nassert (dest->nodecount + moved >= dest->nodecount); /* nassert no weirdness */ nassert (moved <= source->nodecount); source->nodecount -= moved; dest->nodecount += moved; /* nassert list sanity */ nassert (list_verify (source)); nassert (list_verify (dest)); } 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 */ nassert (list_count (sour) + list_count (dest) >= list_count (sour)); /* lists must be sorted */ nassert (list_is_sorted (sour, compare)); nassert (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); } 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); } nassert (list_is_sorted (list, compare)); } 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 */ 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 */ int list_isempty (list_t * list) { return list->nodecount == 0; } /* * Return 1 if the list is full, 0 otherwise * Permitted only on bounded lists. */ int list_isfull (list_t * list) { return list->nodecount == list->maxcount; } /* * Check if the node pool is empty. */ int lnode_pool_isempty (lnodepool_t * pool) { return (pool->fre == NULL); } /* * Add the given node at the end of the list */ 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. */ void list_prepend (list_t * list, lnode_t * node) { list_ins_after (list, node, &list->nilnode); } /* * Retrieve the first node of the list */ 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 */ 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 */ listcount_t list_count (list_t * list) { return list->nodecount; } /* * Remove the first node from the list and return it. */ 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. */ lnode_t * list_del_last (list_t * list) { return list_delete (list, list->nilnode.prev); } /* * Associate a data item with the given node. */ void lnode_put (lnode_t * lnode, void *data) { lnode->data = data; } /* * Retrieve the data item associated with the node. */ void * lnode_get (lnode_t * lnode) { return lnode->data; } /* * Retrieve the node's successor. If there is no successor, * NULL is returned. */ lnode_t * list_next (list_t * list, lnode_t * lnode) { nassert (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(). */ lnode_t * list_prev (list_t * list, lnode_t * lnode) { nassert (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. */ int lnode_is_in_a_list (lnode_t * lnode) { return (lnode->next != NULL || lnode->prev != NULL); } 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; } int comparef (const void *key1, const void *key2) { return strcasecmp (key1, key2); }