diff options
Diffstat (limited to 'src/adlist.c')
-rw-r--r-- | src/adlist.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/src/adlist.c b/src/adlist.c new file mode 100644 index 0000000..f031c46 --- /dev/null +++ b/src/adlist.c @@ -0,0 +1,417 @@ +/* adlist.c - A generic doubly linked list implementation + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + + +#include <stdlib.h> +#include "adlist.h" +#include "zmalloc.h" + +/* Create a new list. The created list can be freed with + * listRelease(), but private value of every node need to be freed + * by the user before to call listRelease(), or by setting a free method using + * listSetFreeMethod. + * + * On error, NULL is returned. Otherwise the pointer to the new list. */ +list *listCreate(void) +{ + struct list *list; + + if ((list = zmalloc(sizeof(*list))) == NULL) + return NULL; + list->head = list->tail = NULL; + list->len = 0; + list->dup = NULL; + list->free = NULL; + list->match = NULL; + return list; +} + +/* Remove all the elements from the list without destroying the list itself. */ +void listEmpty(list *list) +{ + unsigned long len; + listNode *current, *next; + + current = list->head; + len = list->len; + while(len--) { + next = current->next; + if (list->free) list->free(current->value); + zfree(current); + current = next; + } + list->head = list->tail = NULL; + list->len = 0; +} + +/* Free the whole list. + * + * This function can't fail. */ +void listRelease(list *list) +{ + listEmpty(list); + zfree(list); +} + +/* Add a new node to the list, to head, containing the specified 'value' + * pointer as value. + * + * On error, NULL is returned and no operation is performed (i.e. the + * list remains unaltered). + * On success the 'list' pointer you pass to the function is returned. */ +list *listAddNodeHead(list *list, void *value) +{ + listNode *node; + + if ((node = zmalloc(sizeof(*node))) == NULL) + return NULL; + node->value = value; + listLinkNodeHead(list, node); + return list; +} + +/* + * Add a node that has already been allocated to the head of list + */ +void listLinkNodeHead(list* list, listNode *node) { + if (list->len == 0) { + list->head = list->tail = node; + node->prev = node->next = NULL; + } else { + node->prev = NULL; + node->next = list->head; + list->head->prev = node; + list->head = node; + } + list->len++; +} + +/* Add a new node to the list, to tail, containing the specified 'value' + * pointer as value. + * + * On error, NULL is returned and no operation is performed (i.e. the + * list remains unaltered). + * On success the 'list' pointer you pass to the function is returned. */ +list *listAddNodeTail(list *list, void *value) +{ + listNode *node; + + if ((node = zmalloc(sizeof(*node))) == NULL) + return NULL; + node->value = value; + listLinkNodeTail(list, node); + return list; +} + +/* + * Add a node that has already been allocated to the tail of list + */ +void listLinkNodeTail(list *list, listNode *node) { + if (list->len == 0) { + list->head = list->tail = node; + node->prev = node->next = NULL; + } else { + node->prev = list->tail; + node->next = NULL; + list->tail->next = node; + list->tail = node; + } + list->len++; +} + +list *listInsertNode(list *list, listNode *old_node, void *value, int after) { + listNode *node; + + if ((node = zmalloc(sizeof(*node))) == NULL) + return NULL; + node->value = value; + if (after) { + node->prev = old_node; + node->next = old_node->next; + if (list->tail == old_node) { + list->tail = node; + } + } else { + node->next = old_node; + node->prev = old_node->prev; + if (list->head == old_node) { + list->head = node; + } + } + if (node->prev != NULL) { + node->prev->next = node; + } + if (node->next != NULL) { + node->next->prev = node; + } + list->len++; + return list; +} + +/* Remove the specified node from the specified list. + * The node is freed. If free callback is provided the value is freed as well. + * + * This function can't fail. */ +void listDelNode(list *list, listNode *node) +{ + listUnlinkNode(list, node); + if (list->free) list->free(node->value); + zfree(node); +} + +/* + * Remove the specified node from the list without freeing it. + */ +void listUnlinkNode(list *list, listNode *node) { + if (node->prev) + node->prev->next = node->next; + else + list->head = node->next; + if (node->next) + node->next->prev = node->prev; + else + list->tail = node->prev; + + node->next = NULL; + node->prev = NULL; + + list->len--; +} + +/* Returns a list iterator 'iter'. After the initialization every + * call to listNext() will return the next element of the list. + * + * This function can't fail. */ +listIter *listGetIterator(list *list, int direction) +{ + listIter *iter; + + if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL; + if (direction == AL_START_HEAD) + iter->next = list->head; + else + iter->next = list->tail; + iter->direction = direction; + return iter; +} + +/* Release the iterator memory */ +void listReleaseIterator(listIter *iter) { + zfree(iter); +} + +/* Create an iterator in the list private iterator structure */ +void listRewind(list *list, listIter *li) { + li->next = list->head; + li->direction = AL_START_HEAD; +} + +void listRewindTail(list *list, listIter *li) { + li->next = list->tail; + li->direction = AL_START_TAIL; +} + +/* Return the next element of an iterator. + * It's valid to remove the currently returned element using + * listDelNode(), but not to remove other elements. + * + * The function returns a pointer to the next element of the list, + * or NULL if there are no more elements, so the classical usage + * pattern is: + * + * iter = listGetIterator(list,<direction>); + * while ((node = listNext(iter)) != NULL) { + * doSomethingWith(listNodeValue(node)); + * } + * + * */ +listNode *listNext(listIter *iter) +{ + listNode *current = iter->next; + + if (current != NULL) { + if (iter->direction == AL_START_HEAD) + iter->next = current->next; + else + iter->next = current->prev; + } + return current; +} + +/* Duplicate the whole list. On out of memory NULL is returned. + * On success a copy of the original list is returned. + * + * The 'Dup' method set with listSetDupMethod() function is used + * to copy the node value. Otherwise the same pointer value of + * the original node is used as value of the copied node. + * + * The original list both on success or error is never modified. */ +list *listDup(list *orig) +{ + list *copy; + listIter iter; + listNode *node; + + if ((copy = listCreate()) == NULL) + return NULL; + copy->dup = orig->dup; + copy->free = orig->free; + copy->match = orig->match; + listRewind(orig, &iter); + while((node = listNext(&iter)) != NULL) { + void *value; + + if (copy->dup) { + value = copy->dup(node->value); + if (value == NULL) { + listRelease(copy); + return NULL; + } + } else { + value = node->value; + } + + if (listAddNodeTail(copy, value) == NULL) { + /* Free value if dup succeed but listAddNodeTail failed. */ + if (copy->free) copy->free(value); + + listRelease(copy); + return NULL; + } + } + return copy; +} + +/* Search the list for a node matching a given key. + * The match is performed using the 'match' method + * set with listSetMatchMethod(). If no 'match' method + * is set, the 'value' pointer of every node is directly + * compared with the 'key' pointer. + * + * On success the first matching node pointer is returned + * (search starts from head). If no matching node exists + * NULL is returned. */ +listNode *listSearchKey(list *list, void *key) +{ + listIter iter; + listNode *node; + + listRewind(list, &iter); + while((node = listNext(&iter)) != NULL) { + if (list->match) { + if (list->match(node->value, key)) { + return node; + } + } else { + if (key == node->value) { + return node; + } + } + } + return NULL; +} + +/* Return the element at the specified zero-based index + * where 0 is the head, 1 is the element next to head + * and so on. Negative integers are used in order to count + * from the tail, -1 is the last element, -2 the penultimate + * and so on. If the index is out of range NULL is returned. */ +listNode *listIndex(list *list, long index) { + listNode *n; + + if (index < 0) { + index = (-index)-1; + n = list->tail; + while(index-- && n) n = n->prev; + } else { + n = list->head; + while(index-- && n) n = n->next; + } + return n; +} + +/* Rotate the list removing the tail node and inserting it to the head. */ +void listRotateTailToHead(list *list) { + if (listLength(list) <= 1) return; + + /* Detach current tail */ + listNode *tail = list->tail; + list->tail = tail->prev; + list->tail->next = NULL; + /* Move it as head */ + list->head->prev = tail; + tail->prev = NULL; + tail->next = list->head; + list->head = tail; +} + +/* Rotate the list removing the head node and inserting it to the tail. */ +void listRotateHeadToTail(list *list) { + if (listLength(list) <= 1) return; + + listNode *head = list->head; + /* Detach current head */ + list->head = head->next; + list->head->prev = NULL; + /* Move it as tail */ + list->tail->next = head; + head->next = NULL; + head->prev = list->tail; + list->tail = head; +} + +/* Add all the elements of the list 'o' at the end of the + * list 'l'. The list 'other' remains empty but otherwise valid. */ +void listJoin(list *l, list *o) { + if (o->len == 0) return; + + o->head->prev = l->tail; + + if (l->tail) + l->tail->next = o->head; + else + l->head = o->head; + + l->tail = o->tail; + l->len += o->len; + + /* Setup other as an empty list. */ + o->head = o->tail = NULL; + o->len = 0; +} + +/* Initializes the node's value and sets its pointers + * so that it is initially not a member of any list. + */ +void listInitNode(listNode *node, void *value) { + node->prev = NULL; + node->next = NULL; + node->value = value; +} |