Edit:
New version improved with the answers and comments received here:
Generic circular doubly-linked list v2
I have written a linked list library that I can use whenever I need a linked list, so I tried to have all the functionality that one could expect from a linked list.
From the different types of linked lists, I decided that the best one is the circular doubly linked list, which has many advantages, and the only disadvantage that I know is using a little extra space.
How it works:
Initializing the list:
struct Alx_LinkedList *list; if (alx_llist_init(&list)) goto err;
Adding members (and data at the same time):
char x[4] = "Hi!"; if (alx_llist_append(list, (const void *)x, sizeof(x)) < 0) goto err;
Removing an element:
alx_llist_remove_tail(list);
Moving through the list (the pointer called current
):
alx_llist_move_to(list, 7);
(of course, the user can move as always by using the next
and prev
(or head
and tail
) pointers, and assigning them to current
):
list->current = list->current->next;
Editing the data in a node:
double y[5] = {0, 1.1, 1,1,1,}; if (alx_llist_edit_current(list, (const void *)y, sizeof(y))) goto err;
Finding a node:
ptrdiff_t pos; pos = alx_llist_find(list, node);
Get size of the list (nmemb):
ptrdiff_t nmemb; nmemb = list->nmemb;
Remove all nodes:
alx_llist_remove_all(list);
Deinitialize list:
alx_llist_deinit(list);
The functions to add a first element or remove the last element need not be used by the user, as the other functions check if those need to be called and do so internally, but they can still be used if the user wants to.
All functions report errors with negative return values, and non-error but abnormal things may return positive values.
Features:
The data can have any type and any size. The list creates a (malloc
ed) copy of the data, and free
s it automatically so that the user only needs to pass a (const void *) to the data and the size of the data.
The size is always available to the user and updated automatically by the functions (if the user modifies this value, the behavior is undefined!).
Is there any functionallity that you would add, or any improvement to this linked list?
Code:
linked-list.h
:
/****************************************************************************** ******* include guard ******************************************************** ******************************************************************************/ #pragma once /* libalx/extra/alx/linked-list.h */ /****************************************************************************** ******* headers ************************************************************** ******************************************************************************/ #include <stddef.h> /****************************************************************************** ******* macros *************************************************************** ******************************************************************************/ /****************************************************************************** ******* enum ***************************************************************** ******************************************************************************/ /****************************************************************************** ******* struct / union ******************************************************* ******************************************************************************/ struct Alx_LLNode { void *data; struct Alx_LLNode *prev; struct Alx_LLNode *next; }; struct Alx_LinkedList { struct Alx_LLNode *head; struct Alx_LLNode *tail; struct Alx_LLNode *current; ptrdiff_t nmemb; }; /****************************************************************************** ******* prototypes *********************************************************** ******************************************************************************/ __attribute__((nonnull)) int alx_llist_init (struct Alx_LinkedList **list); __attribute__((nonnull)) int alx_llist_deinit (struct Alx_LinkedList *list); __attribute__((nonnull)) int alx_llist_first_element (struct Alx_LinkedList *list, const void *data, size_t size); __attribute__((nonnull)) int alx_llist_remove_last (struct Alx_LinkedList *list); __attribute__((nonnull)) int alx_llist_prepend (struct Alx_LinkedList *list, const void *data, size_t size); __attribute__((nonnull)) int alx_llist_append (struct Alx_LinkedList *list, const void *data, size_t size); __attribute__((nonnull)) int alx_llist_insert_before (struct Alx_LinkedList *list, const void *data, size_t size); __attribute__((nonnull)) int alx_llist_insert_after (struct Alx_LinkedList *list, const void *data, size_t size); __attribute__((nonnull)) int alx_llist_remove_head (struct Alx_LinkedList *list); __attribute__((nonnull)) int alx_llist_remove_tail (struct Alx_LinkedList *list); __attribute__((nonnull)) int alx_llist_remove_current(struct Alx_LinkedList *list); __attribute__((nonnull)) int alx_llist_remove_all (struct Alx_LinkedList *list); __attribute__((nonnull, pure)) ptrdiff_t alx_llist_find (struct Alx_LinkedList *list, struct Alx_LLNode *node); __attribute__((nonnull)) int alx_llist_move_fwd (struct Alx_LinkedList *list, ptrdiff_t n); __attribute__((nonnull)) int alx_llist_move_bwd (struct Alx_LinkedList *list, ptrdiff_t n); __attribute__((nonnull)) int alx_llist_move_to (struct Alx_LinkedList *list, ptrdiff_t pos); __attribute__((nonnull)) int alx_llist_edit_current (struct Alx_LinkedList *list, const void *data, size_t size); /****************************************************************************** ******* inline *************************************************************** ******************************************************************************/ /****************************************************************************** ******* end of file ********************************************************** ******************************************************************************/
linked-list.c
:
/****************************************************************************** ******* headers ************************************************************** ******************************************************************************/ #include "libalx/extra/alx/linked-list.h" #include <stdlib.h> #include <string.h> #include "libalx/base/stdlib/alloc/mallocarrays.h" #include "libalx/base/stdlib/alloc/mallocs.h" #include "libalx/base/stdlib/alloc/reallocs.h" /****************************************************************************** ******* macros *************************************************************** ******************************************************************************/ /****************************************************************************** ******* enum / struct / union ************************************************ ******************************************************************************/ /****************************************************************************** ******* static prototypes **************************************************** ******************************************************************************/ /****************************************************************************** ******* global functions ***************************************************** ******************************************************************************/ int alx_llist_init (struct Alx_LinkedList **list) { if (alx_mallocarrays(list, 1)) return -1; (*list)->head = NULL; (*list)->tail = NULL; (*list)->current = NULL; (*list)->nmemb = 0; return 0; } int alx_llist_deinit (struct Alx_LinkedList *list) { int status; status = alx_llist_remove_all(list); free(list); return status; } int alx_llist_first_element (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (list->nmemb) return -3; if (alx_mallocarrays(&node, 1)) return -1; if (alx_mallocs(&node->data, size)) goto err; memcpy(node->data, data, size); node->prev = node; node->next = node; list->head = node; list->tail = node; list->current = node; list->nmemb = 1; return 0; err: free(node); return -2; } int alx_llist_remove_last (struct Alx_LinkedList *list) { struct Alx_LLNode *node; if (list->nmemb != 1) return -1; node = list->head; free(node->data); list->head = NULL; list->tail = NULL; list->current = NULL; free(node); list->nmemb = 0; return 0; } int alx_llist_prepend (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (!list->nmemb) { alx_llist_first_element(list, data, size); return 1; } if (alx_mallocarrays(&node, 1)) return -1; if (alx_mallocs(&node->data, size)) goto err; memcpy(node->data, data, size); node->prev = list->tail; node->next = list->head; list->head->prev = node; list->tail->next = node; list->head = node; (list->nmemb)++; return 0; err: free(node); return -2; } int alx_llist_append (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (!list->nmemb) { alx_llist_first_element(list, data, size); return 1; } if (alx_mallocarrays(&node, 1)) return -1; if (alx_mallocs(&node->data, size)) goto err; memcpy(node->data, data, size); node->prev = list->tail; node->next = list->head; list->head->prev = node; list->tail->next = node; list->tail = node; (list->nmemb)++; return 0; err: free(node); return -2; } int alx_llist_insert_before (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (!list->nmemb) { alx_llist_first_element(list, data, size); return 1; } if (alx_mallocarrays(&node, 1)) return -1; if (alx_mallocs(&node->data, size)) goto err; memcpy(node->data, data, size); node->prev = list->current->prev; node->next = list->current; list->current->prev->next = node; list->current->prev = node; list->current = node; (list->nmemb)++; return 0; err: free(node); return -2; } int alx_llist_insert_after (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (!list->nmemb) { alx_llist_first_element(list, data, size); return 1; } if (alx_mallocarrays(&node, 1)) return -1; if (alx_mallocs(&node->data, size)) goto err; memcpy(node->data, data, size); node->prev = list->current; node->next = list->current->next; list->current->next->prev = node; list->current->next = node; list->current = node; (list->nmemb)++; return 0; err: free(node); return -2; } int alx_llist_remove_head (struct Alx_LinkedList *list) { struct Alx_LLNode *node; switch (list->nmemb) { case 0: return 1; case 1: return alx_llist_remove_last(list); } node = list->head; free(node->data); list->head->prev->next = node->next; list->head->next->prev = node->prev; if (list->current == list->head) list->current = node->next; list->head = node->next; free(node); (list->nmemb)--; return 0; } int alx_llist_remove_tail (struct Alx_LinkedList *list) { struct Alx_LLNode *node; switch (list->nmemb) { case 0: return 1; case 1: return alx_llist_remove_last(list); } node = list->tail; free(node->data); list->tail->prev->next = node->next; list->tail->next->prev = node->prev; if (list->current == list->tail) list->current = node->prev; list->tail = node->prev; free(node); (list->nmemb)--; return 0; } int alx_llist_remove_current(struct Alx_LinkedList *list) { struct Alx_LLNode *node; switch (list->nmemb) { case 0: return 1; case 1: return alx_llist_remove_last(list); } node = list->current; free(node->data); list->current->prev->next = node->next; list->current->next->prev = node->prev; if (list->tail == list->current) { list->tail = node->prev; list->current = node->prev; } else if (list->head == list->current) { list->head = node->next; list->current = node->next; } else { list->current = node->prev; } free(node); (list->nmemb)--; return 0; } int alx_llist_remove_all (struct Alx_LinkedList *list) { ptrdiff_t n; n = list->nmemb; if (!n) return 1; for (ptrdiff_t i = 0; i < n; i++) alx_llist_remove_tail(list); return 0; } ptrdiff_t alx_llist_find (struct Alx_LinkedList *list, struct Alx_LLNode *node) { struct Alx_LLNode *tmp; tmp = list->head; for (ptrdiff_t i = 0; i < list->nmemb; i++) { if (tmp == node) return i; tmp = tmp->next; } return -1; } int alx_llist_move_fwd (struct Alx_LinkedList *list, ptrdiff_t n) { int status; if (n < 0) return alx_llist_move_bwd(list, -n); status = 0; for (ptrdiff_t i = 0; i < n; i++) { list->current = list->current->next; if (list->current == list->head) status++; } return 0; } int alx_llist_move_bwd (struct Alx_LinkedList *list, ptrdiff_t n) { int status; if (n < 0) return alx_llist_move_fwd(list, -n); status = 0; for (ptrdiff_t i = 0; i < n; i++) { list->current = list->current->prev; if (list->current == list->tail) status--; } return 0; } int alx_llist_move_to (struct Alx_LinkedList *list, ptrdiff_t pos) { list->current = list->head; if (pos < 0) return alx_llist_move_bwd(list, -pos); return alx_llist_move_fwd(list, pos); } int alx_llist_edit_current (struct Alx_LinkedList *list, const void *data, size_t size) { struct Alx_LLNode *node; if (!list->nmemb) return -1; node = list->current; if (alx_reallocs(&node->data, size)) return -2; memmove(node->data, data, size); return 0; } /****************************************************************************** ******* static function definitions ****************************************** ******************************************************************************/ /****************************************************************************** ******* end of file ********************************************************** ******************************************************************************/
Functions and macros used in linked-list.h
:
/* * [[gnu::nonnull]] * int alx_mallocarrays(type **restrict ptr, ptrdiff_t nmemb); */ #define alx_mallocarrays(ptr, nmemb) ( \ { \ __auto_type ptr_ = (ptr); \ \ *ptr_ = alx_mallocarray(nmemb, sizeof(**ptr_)); \ \ !(*ptr_); \ } \ ) inline void *alx_mallocarray (ptrdiff_t nmemb, size_t size) { if (nmemb < 0) goto ovf; if ((size_t)nmemb > (SIZE_MAX / size)) goto ovf; return malloc(size * (size_t)nmemb); ovf: errno = ENOMEM; return NULL; } /* * [[gnu::nonnull]] * int alx_mallocs(void **restrict ptr, size_t size); */ #define alx_mallocs(ptr, size) ( \ { \ __auto_type ptr_ = (ptr); \ \ *ptr_ = malloc(size); \ \ !(*ptr_); \ } \ ) /* * [[gnu::nonnull]] * int alx_reallocs(void **restrict ptr, size_t size); */ #define alx_reallocs(ptr, size) ( \ { \ __auto_type ptr_ = (ptr); \ \ *ptr_ = realloc(*ptr_, size); \ \ !(*ptr_); \ } \ )
Finally, I'm sorry about the tabs. It is aligned to 8 characters. I will add a double tab when I can, so that it looks good.