This repository has been archived on 2025-02-12. You can view files and clone it, but cannot push or open issues or pull requests.
NeoStats/list.c
2004-01-14 11:36:37 +00:00

848 lines
17 KiB
C

/* 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 <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$
* $Name: $
*/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#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);
}