/* * linked list routines * by Matthew Luckie * * Copyright (C) 2004-2007 Matthew Luckie. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Matthew Luckie ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Matthew Luckie BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: mjl_list.c,v 1.46 2007/05/09 22:47:42 mjl Exp $ * */ #include #include #if defined(DMALLOC) #include #endif #include "mjl_list.h" struct slist_node { void *item; struct slist_node *next; }; struct dlist_node { void *item; struct dlist_node *prev; struct dlist_node *next; struct dlist *list; }; struct clist_node { void *item; struct clist_node *prev; struct clist_node *next; }; struct slist { slist_node_t *head; slist_node_t *tail; int length; unsigned int lock; }; struct dlist { dlist_node_t *head; dlist_node_t *tail; int length; unsigned int lock; }; struct clist { clist_node_t *head; int length; unsigned int lock; }; #if !defined(NDEBUG) && defined(MJLLIST_DEBUG) static void slist_assert(const slist_t *list) { slist_node_t *node; int i; assert(list->length >= 0); if(list->length == 0) { assert(list->head == NULL); assert(list->tail == NULL); } else if(list->length == 1) { assert(list->head != NULL); assert(list->tail != NULL); assert(list->head == list->tail); assert(list->head->next == NULL); } else { i = 1; node = list->head; while(ilength) { assert(node != NULL); node = node->next; i++; } assert(node == list->tail); } return; } #else #define slist_assert(list)((void)0) #endif void slist_lock(slist_t *list) { list->lock++; return; } void slist_unlock(slist_t *list) { assert(list->lock > 0); list->lock--; return; } int slist_islocked(slist_t *list) { return list->lock == 0 ? 0 : 1; } slist_t *slist_alloc() { slist_t *list; if((list = (slist_t *)malloc(sizeof(slist_t))) != NULL) { list->head = NULL; list->tail = NULL; list->length = 0; list->lock = 0; } return list; } static slist_node_t *slist_node(void *item, slist_node_t *next) { slist_node_t *node; if((node = malloc(sizeof(slist_node_t))) != NULL) { node->item = item; node->next = next; } return node; } /* * slist_dup * * make a copy of the list that points to the same items. * if the foreach function is not null, call that on each item too. */ slist_t *slist_dup(slist_t *oldlist, const slist_foreach_t func, void *param) { slist_t *list = (slist_t *)malloc(sizeof(slist_t)); slist_node_t *oldnode, *node; /* first, allocate a replacement slist_t structure */ if((list = (slist_t *)malloc(sizeof(slist_t))) != NULL) { list->head = NULL; list->tail = NULL; list->length = 0; } else return NULL; if(oldlist->head != NULL) { if((node = slist_node(oldlist->head->item, NULL)) == NULL) { goto err; } if(func != NULL) func(oldlist->head->item, param); list->length = oldlist->length; list->head = node; oldnode = oldlist->head->next; } else return list; while(oldnode != NULL) { if((node->next = slist_node(oldnode->item, NULL)) == NULL) { goto err; } if(func != NULL) func(oldnode->item, param); oldnode = oldnode->next; node = node->next; } list->tail = node; return list; err: slist_free(list); return NULL; } void slist_concat(slist_t *first, slist_t *second) { assert(first->lock == 0); assert(second->lock == 0); slist_assert(first); slist_assert(second); /* if there is nothing to concatenate, then return now */ if(second->length == 0) { return; } /* shift the second list's nodes into the first */ if(first->tail != NULL) { first->tail->next = second->head; first->length += second->length; first->tail = second->tail; } else { first->head = second->head; first->tail = second->tail; first->length = second->length; } /* reset the second list */ second->length = 0; second->head = NULL; second->tail = NULL; slist_assert(first); slist_assert(second); return; } void slist_free(slist_t *list) { slist_node_t *node = list->head; slist_node_t *next; slist_assert(list); assert(list->lock == 0); while(node != NULL) { next = node->next; free(node); node = next; } free(list); return; } void *slist_head_push(slist_t *list, void *item) { slist_node_t *node; slist_assert(list); assert(list->lock == 0); if((node = slist_node(item, list->head)) != NULL) { list->head = node; if(list->tail == NULL) { list->tail = node; } list->length++; } slist_assert(list); return node; } void *slist_tail_push(slist_t *list, void *item) { slist_node_t *node; slist_assert(list); assert(list->lock == 0); if((node = slist_node(item, NULL)) != NULL) { if(list->tail != NULL) { list->tail->next = node; list->tail = node; } else { list->head = list->tail = node; } list->length++; } slist_assert(list); return node; } void *slist_head_pop(slist_t *list) { slist_node_t *node; void *item = NULL; slist_assert(list); assert(list->lock == 0); if(list->head != NULL) { node = list->head; item = node->item; /* if there are no nodes left ... */ if((list->head = node->next) == NULL) { list->tail = NULL; } free(node); list->length--; } slist_assert(list); return item; } void *slist_head_get(const slist_t *list) { if(list->head == NULL) return NULL; return list->head->item; } slist_node_t *slist_head_node(const slist_t *list) { return list->head; } void *slist_node_item(const slist_node_t *node) { return node->item; } slist_node_t *slist_node_next(const slist_node_t *node) { return node->next; } int slist_foreach(slist_t *list, const slist_foreach_t func, void *param) { slist_node_t *node = list->head; slist_node_t *next; slist_lock(list); slist_assert(list); while(node != NULL) { next = node->next; if(func(node->item, param) != 0) { slist_unlock(list); return -1; } node = next; } slist_assert(list); slist_unlock(list); return 0; } int slist_count(const slist_t *list) { slist_assert(list); return list->length; } #if !defined(NDEBUG) && defined(MJLLIST_DEBUG) static void dlist_assert(const dlist_t *list) { dlist_node_t *node; int i; if(list == NULL) { return; } assert(list->length >= 0); if(list->length == 0) { assert(list->head == NULL); assert(list->tail == NULL); } else if(list->length == 1) { assert(list->head != NULL); assert(list->tail != NULL); assert(list->head == list->tail); assert(list->head->next == NULL); assert(list->head->prev == NULL); assert(list->head->list == list); } else { assert(list->head->prev == NULL); assert(list->tail->next == NULL); i = 1; node = list->head; while(i < list->length-1) { assert(node != NULL); assert(node->next != NULL); assert(node->next->prev == node); assert(node->list == list); node = node->next; i++; } assert(node->next == list->tail); assert(node->list == list); } return; } #else #define dlist_assert(list)((void)0) #endif void dlist_lock(dlist_t *list) { assert(list != NULL); list->lock++; return; } void dlist_unlock(dlist_t *list) { assert(list != NULL); assert(list->lock > 0); list->lock--; return; } int dlist_islocked(dlist_t *list) { assert(list != NULL); return list->lock == 0 ? 0 : 1; } dlist_t *dlist_alloc() { dlist_t *list; if((list = (dlist_t *)malloc(sizeof(dlist_t))) != NULL) { list->head = NULL; list->tail = NULL; list->length = 0; list->lock = 0; } return list; } static dlist_node_t *dlist_node(void *i, dlist_node_t *p, dlist_node_t *n) { dlist_node_t *node; if((node = (dlist_node_t *)malloc(sizeof(dlist_node_t))) != NULL) { node->item = i; node->prev = p; node->next = n; node->list = NULL; } return node; } dlist_node_t *dlist_node_alloc(void *item) { return dlist_node(item, NULL, NULL); } void dlist_free(dlist_t *list) { dlist_node_t *node = list->head; dlist_node_t *next; dlist_assert(list); assert(list->lock == 0); while(node != NULL) { next = node->next; free(node); node = next; } free(list); return; } void dlist_concat(dlist_t *first, dlist_t *second) { dlist_node_t *p; assert(first != NULL); assert(first->lock == 0); assert(second != NULL); assert(second->lock == 0); dlist_assert(first); dlist_assert(second); /* if there's nothing to concatenate, then stop now */ if(second->length == 0) { return; } else { for(p = second->head; p != NULL; p = p->next) { p->list = first; } } /* shift the second list's nodes into the first */ if(first->tail != NULL) { first->tail->next = second->head; second->head->prev = first->tail; first->tail = second->tail; first->length += second->length; } else { first->head = second->head; first->tail = second->tail; first->length = second->length; } /* reset the second list */ second->length = 0; second->head = NULL; second->tail = NULL; return; } void dlist_node_head_push(dlist_t *list, dlist_node_t *node) { assert(list != NULL); assert(list->lock == 0); assert(node != NULL); dlist_assert(list); /* eject the node from whatever list it is currently on */ dlist_node_eject(node->list, node); /* if we don't have a head node, we don't have a tail node set either */ if(list->head == NULL) { list->tail = node; } else { list->head->prev = node; } /* the current head node will be second on the list */ node->next = list->head; node->list = list; list->head = node; list->length++; dlist_assert(list); return; } void dlist_node_tail_push(dlist_t *list, dlist_node_t *node) { assert(list != NULL); assert(list->lock == 0); assert(node != NULL); dlist_assert(list); /* eject the node from whatever list it is currently on */ dlist_node_eject(node->list, node); /* if we don't have a tail node, we don't have a head node set either */ if(list->tail == NULL) { list->head = node; } else { list->tail->next = node; } /* the current tail node will be second to last on the list */ node->prev = list->tail; node->list = list; list->tail = node; list->length++; dlist_assert(list); return; } void *dlist_head_push(dlist_t *list, void *item) { dlist_node_t *node; assert(list != NULL); assert(list->lock == 0); if((node = dlist_node(item, NULL, NULL)) != NULL) { dlist_node_head_push(list, node); } return node; } void *dlist_tail_push(dlist_t *list, void *item) { dlist_node_t *node; assert(list != NULL); assert(list->lock == 0); if((node = dlist_node(item, NULL, NULL)) != NULL) { dlist_node_tail_push(list, node); } return node; } void *dlist_head_pop(dlist_t *list) { dlist_node_t *node; void *item; assert(list != NULL); assert(list->lock == 0); dlist_assert(list); if(list->head == NULL) { return NULL; } node = list->head; item = node->item; /* * if we have a non-null node to replace the head with, null its prev * pointer as the node is now at the head of the list */ if((list->head = node->next) != NULL) { list->head->prev = NULL; } else { /* no nodes left in list */ list->tail = NULL; } free(node); list->length--; dlist_assert(list); return item; } void *dlist_tail_pop(dlist_t *list) { dlist_node_t *node; void *item; assert(list != NULL); assert(list->lock == 0); dlist_assert(list); if(list->head == NULL) { return NULL; } node = list->tail; item = node->item; list->tail = node->prev; if(list->tail != NULL) { list->tail->next = NULL; } if(list->head == node) { list->head = NULL; } free(node); list->length--; dlist_assert(list); return item; } /* * dlist_node_detach * * a node is on a list. detach it from the list, but do not free the * node. */ static void dlist_node_detach(dlist_t *list, dlist_node_t *node) { assert(node != NULL); assert(list == NULL || list->lock == 0); assert(node->list == list); /* if the node is in the list, then we have to detach it */ if(node->prev != NULL || node->next != NULL || (list != NULL && list->head == node)) { if(list->head == node) { list->head = node->next; } if(list->tail == node) { list->tail = node->prev; } if(node->prev != NULL) node->prev->next = node->next; if(node->next != NULL) node->next->prev = node->prev; list->length--; /* node has been detached, reset its pointers */ node->next = NULL; node->prev = NULL; node->list = NULL; } return; } /* * dlist_node_pop * */ void *dlist_node_pop(dlist_t *list, dlist_node_t *node) { void *item; assert(node != NULL); assert(node->list == list); assert(list == NULL || list->lock == 0); dlist_assert(list); dlist_node_detach(list, node); item = node->item; free(node); dlist_assert(list); return item; } void *dlist_node_item(const dlist_node_t *node) { assert(node != NULL); return node->item; } dlist_node_t *dlist_node_next(const dlist_node_t *node) { assert(node != NULL); return node->next; } dlist_node_t *dlist_node_prev(const dlist_node_t *node) { assert(node != NULL); return node->prev; } /* * dlist_node_eject * * remove a specified dlist_node from the list. do not actually free the * node structure itself, though. */ void dlist_node_eject(dlist_t *list, dlist_node_t *node) { assert(node != NULL); assert(list == NULL || list->lock == 0); assert(list == node->list); dlist_assert(list); dlist_node_detach(list, node); dlist_assert(list); return; } void *dlist_head_get(const dlist_t *list) { assert(list != NULL); if(list->head == NULL) return NULL; return list->head->item; } dlist_node_t *dlist_head_node(const dlist_t *list) { assert(list != NULL); return list->head; } void *dlist_tail_get(const dlist_t *list) { assert(list != NULL); if(list->tail == NULL) return NULL; return list->tail->item; } int dlist_foreach(dlist_t *list, const dlist_foreach_t func, void *param) { dlist_node_t *node, *next; assert(list != NULL); assert(func != NULL); dlist_lock(list); dlist_assert(list); node = list->head; while(node != NULL) { next = node->next; if(func(node->item, param) != 0) { dlist_unlock(list); return -1; } node = next; } dlist_assert(list); dlist_unlock(list); return 0; } int dlist_count(const dlist_t *list) { assert(list != NULL); dlist_assert(list); return list->length; } #if !defined(NDEBUG) && defined(MJLLIST_DEBUG) static void clist_assert(const clist_t *list) { clist_node_t *node; int i; assert(list->length >= 0); if(list->length == 0) { assert(list->head == NULL); } else if(list->length == 1) { assert(list->head != NULL); assert(list->head->next == list->head); assert(list->head->prev == list->head); } else { i = 1; node = list->head; while(i < list->length) { assert(node != NULL); assert(node->next != NULL); assert(node->next->prev == node); node = node->next; i++; } assert(node->next == list->head); } return; } #else #define clist_assert(list)((void)0) #endif clist_t *clist_alloc() { clist_t *list; if((list = (clist_t *)malloc(sizeof(clist_t))) != NULL) { list->head = NULL; list->length = 0; list->lock = 0; } return list; } void clist_lock(clist_t *list) { list->lock++; return; } void clist_unlock(clist_t *list) { assert(list->lock > 0); list->lock--; return; } int clist_islocked(clist_t *list) { return list->lock == 0 ? 0 : 1; } void clist_free(clist_t *list) { clist_node_t *node; clist_node_t *next; clist_assert(list); if((node = list->head) != NULL) { /* break the circle */ list->head->prev->next = NULL; /* delete all the nodes */ while(node != NULL) { next = node->next; free(node); node = next; } } free(list); return; } clist_node_t *clist_tail_push(clist_t *list, void *item) { clist_node_t *node, *tail; clist_assert(list); if((node = (clist_node_t *)malloc(sizeof(clist_node_t))) == NULL) { return NULL; } node->item = item; if(list->head != NULL) { tail = list->head->prev; node->prev = tail; node->next = list->head; tail->next = node; list->head->prev = node; } else { list->head = node; node->prev = node->next = node; } list->length++; clist_assert(list); return node; } clist_node_t *clist_head_push(clist_t *list, void *item) { clist_node_t *node; if((node = clist_tail_push(list, item)) != NULL) { list->head = node; } return node; } void *clist_node_pop(clist_t *list, clist_node_t *node) { void *item; assert(list->lock == 0); clist_assert(list); item = node->item; if(node == node->prev) { list->head = NULL; } else { if(list->head == node) { list->head = node->next; } node->prev->next = node->next; node->next->prev = node->prev; } free(node); list->length--; clist_assert(list); return item; } void *clist_tail_pop(clist_t *list) { if(list->head == NULL) { return NULL; } return clist_node_pop(list, list->head->prev); } void *clist_head_pop(clist_t *list) { if(list->head == NULL) { return NULL; } return clist_node_pop(list, list->head); } void *clist_node_item(const clist_node_t *node) { return node->item; } clist_node_t *clist_node_next(const clist_node_t *node) { return node->next; } clist_node_t *clist_head_left(clist_t *list) { if(list->head == NULL) { return NULL; } list->head = list->head->prev; return list->head; } clist_node_t *clist_head_right(clist_t *list) { if(list->head == NULL) { return NULL; } list->head = list->head->next; return list->head; } int clist_foreach(clist_t *list, const clist_foreach_t func, void *param) { clist_node_t *node = list->head; clist_node_t *next; clist_lock(list); clist_assert(list); if(node == NULL) { return 0; } for(;;) { next = node->next; if(func(node->item, param) != 0) { clist_unlock(list); return -1; } if(next != list->head) { node = next; } else break; } clist_assert(list); clist_unlock(list); return 0; } int clist_count(const clist_t *list) { clist_assert(list); return list->length; }