/* ################################################################### */ /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */ /* */ /* This Source Code Form is subject to the terms of the Mozilla Public */ /* License, v. 2.0. If a copy of the MPL was not distributed with this */ /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /* ################################################################### */ /* ********************************************************************* */ /* Tiny linked list implementation. */ /* */ /* Each node contain a void pointer to some opaque data, these functions */ /* will not try to allocate or free this data pointer. */ /* */ /* Also accessors are not provided, the user has to directly manipulate */ /* the structure members (head, tail, len, data, prev, next). */ /* ********************************************************************* */ #include #include #include #include #include #include "xmalloc.h" #include "list.h" static ll_node_t * ll_partition(ll_node_t *l, ll_node_t *h, int (*comp)(void const *, void const *), void (*swap)(void **, void **)); static void ll_quicksort(ll_node_t *l, ll_node_t *h, int (*comp)(void const *, void const *), void (*swap)(void **, void **)); /* ========================== */ /* Creates a new linked list. */ /* ========================== */ ll_t * ll_new(void) { ll_t *ret = xmalloc(sizeof(ll_t)); ll_init(ret); return ret; } /* ========================== */ /* Initializes a linked list. */ /* ========================== */ void ll_init(ll_t *list) { list->head = NULL; list->tail = NULL; list->len = 0; } /* ====================================================== */ /* Allocates the space for a new node in the linked list. */ /* ====================================================== */ ll_node_t * ll_new_node(void) { return xmalloc(sizeof(ll_node_t)); } /* ====================================================================== */ /* Appends a new node filled with its data at the end of the linked list. */ /* The user is responsible for the memory management of the data. */ /* */ /* Note: list is assumed to be initialized by ll_new(). */ /* ====================================================================== */ void ll_append(ll_t * const list, void * const data) { ll_node_t *node; node = ll_new_node(); /* ll_new_node cannot return NULL because it * | uses xmalloc which does not return if there * | is an allocation error. */ node->data = data; node->next = NULL; /* This node will be the last. */ node->prev = list->tail; /* NULL if it is a new list. */ if (list->tail != NULL) list->tail->next = node; else list->head = node; list->tail = node; ++list->len; /* One more node in the list. */ } /* ================================== */ /* Removes a node from a linked list. */ /* ================================== */ int ll_delete(ll_t * const list, ll_node_t *node) { if (list->head == list->tail) { /* We delete the last remaining element from the list. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */ if (list->head == NULL) return 0; list->head = list->tail = NULL; } else if (node->prev == NULL) { /* We delete the first element from the list. */ /* """""""""""""""""""""""""""""""""""""""""" */ list->head = node->next; list->head->prev = NULL; } else if (node->next == NULL) { /* We delete the last element from the list. */ /* """"""""""""""""""""""""""""""""""""""""" */ list->tail = node->prev; list->tail->next = NULL; } else { /* We delete an element from the list. */ /* """"""""""""""""""""""""""""""""""" */ node->next->prev = node->prev; node->prev->next = node->next; } free(node); --list->len; /* One less node in the list. */ return 1; } /* ====================================================== */ /* Partition code for the quicksort function. */ /* Based on code found here: */ /* http://www.geeksforgeeks.org/quicksort-for-linked-list */ /* ====================================================== */ static ll_node_t * ll_partition(ll_node_t *l, ll_node_t *h, int (*comp)(void const *, void const *), void (*swap)(void **, void **)) { /* Considers last element as pivot, places the pivot element at its */ /* correct position in sorted array, and places all smaller (smaller than */ /* pivot) to left of pivot and all greater elements to right of pivot. */ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ /* Set pivot as h element. */ /* """"""""""""""""""""""" */ void *x = h->data; ll_node_t *i = l->prev; ll_node_t *j; for (j = l; j != h; j = j->next) { if (comp(j->data, x) < 1) { i = (i == NULL) ? l : i->next; swap(&(i->data), &(j->data)); } } i = (i == NULL) ? l : i->next; swap(&(i->data), &(h->data)); return i; } /* ======================================================== */ /* A recursive implementation of quicksort for linked list. */ /* Based on code found here: */ /* http://www.geeksforgeeks.org/quicksort-for-linked-list */ /* ======================================================== */ static void ll_quicksort(ll_node_t *l, ll_node_t *h, int (*comp)(void const *, void const *), void (*swap)(void **, void **)) { if (h != NULL && l != h && l != h->next) { ll_node_t *p = ll_partition(l, h, comp, swap); ll_quicksort(l, p->prev, comp, swap); ll_quicksort(p->next, h, comp, swap); } } /* ============================ */ /* A linked list sort function. */ /* ============================ */ void ll_sort(ll_t *list, int (*comp)(void const *, void const *), void (*swap)(void **, void **)) { /* Call the recursive ll_quicksort function. */ /* """"""""""""""""""""""""""""""""""""""""" */ ll_quicksort(list->head, list->tail, comp, swap); } /* ==========================================================================*/ /* Finds a node in the list containing data. Return the node pointer or NULL */ /* if not found. */ /* A comparison function must be provided to compare a and b (strcmp like). */ /* ==========================================================================*/ ll_node_t * ll_find(ll_t * const list, void * const data, int (*cmpfunc)(const void *, const void *)) { ll_node_t *node; if (NULL == (node = list->head)) return NULL; do { if (0 == cmpfunc(node->data, data)) return node; } while (NULL != (node = node->next)); return NULL; } /* =============================================== */ /* Free all the elements of a list (make it empty) */ /* NULL or a custom function may be used to free */ /* the sub components of the elements. */ /* =============================================== */ void ll_free(ll_t * const list, void (*clean)(void *)) { if (list != NULL) { ll_node_t *node = list->head; while (node) { /* Apply a custom cleaner if not NULL. */ /* """"""""""""""""""""""""""""""""""" */ if (clean) clean(node->data); ll_delete(list, node); node = list->head; } } } /* ==================================== */ /* Destroy a list and all its elements. */ /* ==================================== */ void ll_destroy(ll_t *list, void (*clean)(void *)) { if (list != NULL) { ll_free(list, clean); free(list); } }