/* $Id: list.c,v 1.13 2003/06/15 10:05:55 sjoerd Exp $ */ #include #include #include #include #include "common.h" #include "list.h" /* * Simple refcounted linked list implementation. * Copyright (C) 2002-2006 Sjoerd Simons * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ static void list_node_ref(List * list, List_node * item) { item->refcount++; } static void list_node_unref(List * list, List_node * item) { item->refcount--; if (item->refcount <= 0) { if (item->next != NULL) { item->next->prev = item->prev; } if (item->prev != NULL) { item->prev->next = item->next; } if (list->head == item) { list->head = item->next; } if (list->tail == item) { list->tail = item->prev; } free(item); } } static List_node * list_new_node(void *data) { List_node *result = malloc(sizeof(List_node)); assert(data != NULL); assert(result != NULL); memset(result, 0, sizeof(List_node)); result->data = data; return result; } static void list_del_node(List * list, List_node * item) { item->data = NULL; list->length--; list_node_unref(list, item); } List * new_list(void) { List *result; result = malloc(sizeof(List)); assert(result != NULL); memset(result, 0, sizeof(List)); return result; } void del_list(List * list) { List_ptr *ptr; ptr = new_list_ptr(list); if (list_ptr_first(ptr)) { do { list_ptr_del_current(ptr); } while (list_ptr_next(ptr)); } del_list_ptr(ptr); free(list); } int list_prepend(List * list, void *data) { List_node *n; n = list_new_node(data); list_node_ref(list, n); if (list->tail == NULL) { list->tail = n; } else { /* list->tail != NULL so list->head is also !NULL */ n->next = list->head; list->head->prev = n; } list->head = n; list->length++; return TRUE; } int list_append(List * list, void *data) { List_node *n; n = list_new_node(data); list_node_ref(list, n); if (list->head == NULL) { list->head = n; } else { /* list->head != NULL so list->tail is also !NULL */ n->prev = list->tail; list->tail->next = n; } list->tail = n; list->length++; return TRUE; } int list_cmp_data(void *data, void *user_data) { return data == user_data; } int list_del(List * list, void *data) { List_ptr *ptr; ptr = new_list_ptr(list); if (!list_ptr_first(ptr)) { del_list_ptr(ptr); return FALSE; } do { if (list_ptr_get_data(ptr) == data) { list_ptr_del_current(ptr); del_list_ptr(ptr); return TRUE; } } while (list_ptr_next(ptr)); del_list_ptr(ptr); return FALSE; } void * list_search(List * list, ListFunc cmp_func, void *user_data) { List_ptr *ptr; void *data; ptr = new_list_ptr(list); if (!list_ptr_first(ptr)) { del_list_ptr(ptr); return NULL; } do { data = list_ptr_get_data(ptr); if (cmp_func(data, user_data)) { del_list_ptr(ptr); return data; } } while (list_ptr_next(ptr)); del_list_ptr(ptr); return NULL; } int list_foreach(List * list, ListFunc do_func, void *user_data) { List_ptr *ptr; void *data; ptr = new_list_ptr(list); if (!list_ptr_first(ptr)) { del_list_ptr(ptr); return TRUE; } do { data = list_ptr_get_data(ptr); if (!do_func(data, user_data)) { del_list_ptr(ptr); return FALSE; } } while (list_ptr_next(ptr)); del_list_ptr(ptr); return TRUE; } List_ptr * new_list_ptr(List * list) { List_ptr *result; result = malloc(sizeof(List_ptr)); assert(result != NULL); result->list = list; result->current = NULL; return result; } void del_list_ptr(List_ptr * ptr) { if (ptr->current != NULL) { list_node_unref(ptr->list, ptr->current); } free(ptr); } List_ptr * list_ptr_dup(List_ptr * ptr) { List_ptr *result; result = malloc(sizeof(List_ptr)); assert(result != NULL); memcpy(result, ptr, sizeof(List_ptr)); list_node_ref(result->list, result->current); return result; } void * list_ptr_get_data(List_ptr * ptr) { return ptr->current == NULL ? NULL : ptr->current->data; } void list_ptr_del_current(List_ptr * ptr) { if (ptr->current != NULL && ptr->current->data != NULL) { list_del_node(ptr->list, ptr->current); } } static int list_ptr_getend(List_ptr * ptr, int head) { List_node *next; if (head) { next = ptr->list->head; } else { next = ptr->list->tail; } while (next != NULL && next->data == NULL) { if (head) { next = next->next; } else { next = next->prev; } } if (next == NULL) { return FALSE; } if (ptr->current != NULL) { list_node_unref(ptr->list, ptr->current); } ptr->current = next; list_node_ref(ptr->list, ptr->current); return TRUE; } int list_ptr_first(List_ptr * ptr) { return list_ptr_getend(ptr, TRUE); } int list_ptr_last(List_ptr * ptr) { return list_ptr_getend(ptr, FALSE); } static int list_ptr_step(List_ptr * ptr, int forward) { List_node *next; if (ptr->current == NULL) { return FALSE; } next = ptr->current->next; while (next != NULL && next->data == NULL) { if (forward) { next = next->next; } else { next = next->prev; } } if (next == NULL) { return FALSE; } list_node_unref(ptr->list, ptr->current); ptr->current = next; list_node_ref(ptr->list, ptr->current); return TRUE; } int list_ptr_next(List_ptr * ptr) { return list_ptr_step(ptr, TRUE); } int list_ptr_prev(List_ptr * ptr) { return list_ptr_step(ptr, FALSE); } void * list_pop(List *list) { void *result; List_node *node; for (node = list->head; node != NULL && node->data == NULL; node = node->next); if (node == NULL) return NULL; result = node->data; list_del_node(list,node); return result; } int list_length(List *list) { return list->length; } void list_swapnodes(List_node *node0,List_node *node1) { List_node *tmp; if (node0->next == node1) { node0->next = node1->next; node1->next = node0; tmp = node0->prev; node0->prev = node1; node1->prev = tmp; } else if (node1->next == node0) { node1->next = node0->next; node0->next = node1; tmp = node1->prev; node1->prev = node0; node0->prev = tmp; } else { tmp = node1->next; node1->next = node0->next; node0->next = tmp; tmp = node1->prev; node1->prev = node0->prev; node0->prev = tmp; } if (node1->next != NULL) { node1->next->prev = node1; } if (node0->next != NULL) { node0->next->prev = node0; } if (node0->prev != NULL) { node0->prev->next = node0; } if (node1->prev != NULL) { node1->prev->next = node1; } } List_node * list_partition(ListSortFunc func,List_node **snode, List_node **enode,void *user_data) { List_node *node; List_node *tmp; List_node *pivot = *enode; List_node *pivotpoint = *snode;; for (node = *snode ; node != *enode; node = node->next) { if (func(node->data,pivot->data,user_data) <= 0) { if (node != pivotpoint) { list_swapnodes(node,pivotpoint); if (*snode == pivotpoint) { *snode = node; } else if (*snode == node) { *snode = pivotpoint; } } tmp = pivotpoint; pivotpoint = node->next; node = tmp; } } list_swapnodes(pivot,pivotpoint); if (*snode == pivotpoint) { *snode = pivot; } *enode = pivotpoint; return pivot; } int list_do_sort(ListSortFunc func, List_node *snode, List_node *enode,void *user_data) { List_node *node; if (snode != NULL && enode != NULL && snode->prev != enode && snode != enode) { node = list_partition(func,&snode,&enode,user_data); list_do_sort(func,snode,node->prev,user_data); list_do_sort(func,node->next,enode,user_data); } return TRUE; } int list_sort(List *list,ListSortFunc func,void *user_data) { List_node *node; if (list->length == 0) return TRUE; list_do_sort(func,list->head,list->tail,user_data); /* fix -> head pointer */ for (node = list->head; node->prev != NULL; node = node->prev) ; list->head = node; /* fix ->tail pointer */ for (node = list->tail; node->next != NULL; node = node->next) ; list->tail = node; return TRUE; }