| /* Manipulate doubly linked lists. |
| Copyright (C) 2025 Free Software Foundation, Inc. |
| |
| 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 3 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, see <http://www.gnu.org/licenses/>. */ |
| |
| |
| #ifndef _DOUBLY_LINKED_LIST_H |
| #define _DOUBLY_LINKED_LIST_H |
| |
| /* Doubly linked list implementation enforcing typing. |
| |
| This implementation of doubly linked list tries to achieve the enforcement of |
| typing similarly to C++ templates, but without encapsulation. |
| |
| All the functions are prefixed with the type of the value: "AType_xxx". |
| Some functions are prefixed with "_AType_xxx" and are not part of the public |
| API, so should not be used, except for _##LTYPE##_merge_sort with a caveat |
| (see note above its definition). |
| |
| Each function (### is a placeholder for method name) has a macro for: |
| (1) its invocation LINKED_LIST_###(LTYPE). |
| (2) its prototype LINKED_LIST_DECL_###(A, A2, scope). To add in a header |
| file, or a source file for forward declaration. 'scope' should be set |
| respectively to 'extern', or 'static'. |
| (3) its definition LINKED_LIST_DEFN_###(A, A2, scope). To add in a source |
| file with the 'scope' set respectively to nothing, or 'static' depending |
| on (2). |
| |
| Data structures requirements: |
| - LTYPE corresponds to the node of a doubly linked list. It needs to define |
| attributes 'prev' and 'next' which are pointers on the type of a node. |
| For instance: |
| struct my_list_node |
| { |
| T value; |
| struct my_list_node *prev; |
| struct my_list_node *next; |
| }; |
| - LWRAPPERTYPE is a structure wrapping the nodes and others metadata (first, |
| last, size). |
| */ |
| |
| |
| /* Mutative operations: |
| - append |
| - prepend |
| - insert_before |
| - pop_front |
| - pop_back |
| - remove |
| - swap |
| The header and body of each of those operation can be declared individually, |
| or as a whole via LINKED_LIST_MUTATIVE_OPS_PROTOTYPE for the prototypes, and |
| LINKED_LIST_MUTATIVE_OPS_DECL for the implementations. */ |
| |
| /* Append the given node new_ to the exising list. |
| Precondition: prev and next of new_ must be NULL. */ |
| #define LINKED_LIST_APPEND(LTYPE) LTYPE##_append |
| |
| #define LINKED_LIST_DECL_APPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_append (LWRAPPERTYPE *wrapper, LTYPE *new_) |
| |
| #define LINKED_LIST_DEFN_APPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_append (LWRAPPERTYPE *wrapper, LTYPE *new_) \ |
| { \ |
| if (wrapper->last == NULL) \ |
| wrapper->first = new_; \ |
| else \ |
| { \ |
| new_->prev = wrapper->last; \ |
| wrapper->last->next = new_; \ |
| } \ |
| wrapper->last = new_; \ |
| ++wrapper->size; \ |
| } |
| |
| /* Prepend the given node new_ to the existing list. |
| Precondition: prev and next of new_ must be NULL. */ |
| #define LINKED_LIST_PREPEND(LTYPE) LTYPE##_prepend |
| |
| #define LINKED_LIST_DECL_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_prepend (LWRAPPERTYPE *wrapper, LTYPE *new_) |
| |
| #define LINKED_LIST_DEFN_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_prepend (LWRAPPERTYPE *wrapper, LTYPE *new_) \ |
| { \ |
| if (wrapper->first == NULL) \ |
| wrapper->last = new_; \ |
| else \ |
| { \ |
| new_->next = wrapper->first; \ |
| wrapper->first->prev = new_; \ |
| } \ |
| wrapper->first = new_; \ |
| ++wrapper->size; \ |
| } |
| |
| /* Insert the given node new_ before 'where' in the existing list. |
| If where == NULL, the insertion is equivalent to an append. |
| If where == first, the insertion is equivalent to a prepend. */ |
| #define LINKED_LIST_INSERT_BEFORE(LTYPE) LTYPE##_insert_before |
| |
| #define LINKED_LIST_DECL_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_insert_before (LWRAPPERTYPE *wrapper, \ |
| LTYPE *new_, \ |
| LTYPE *where) |
| |
| #define LINKED_LIST_DEFN_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_insert_before (LWRAPPERTYPE *wrapper, \ |
| LTYPE *new_, \ |
| LTYPE *where) \ |
| { \ |
| if (where == wrapper->first) \ |
| LTYPE##_prepend (wrapper, new_); \ |
| else if (where == NULL) \ |
| LTYPE##_append (wrapper, new_); \ |
| else \ |
| { \ |
| where->prev->next = new_; \ |
| new_->prev = where->prev; \ |
| where->prev = new_; \ |
| new_->next = where; \ |
| ++wrapper->size; \ |
| } \ |
| } |
| |
| /* Pop the first node of the list. */ |
| #define LINKED_LIST_POP_FRONT(LTYPE) LTYPE##_pop_front |
| |
| #define LINKED_LIST_DECL_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_pop_front (LWRAPPERTYPE *wrapper) |
| |
| #define LINKED_LIST_DEFN_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_pop_front (LWRAPPERTYPE *wrapper) \ |
| { \ |
| LTYPE *front_node = wrapper->first; \ |
| if (front_node != NULL) \ |
| { \ |
| wrapper->first = front_node->next; \ |
| if (wrapper->last == front_node) \ |
| wrapper->last = NULL; \ |
| else \ |
| { \ |
| front_node->next->prev = NULL; \ |
| front_node->next = NULL; \ |
| } \ |
| front_node->next = NULL; \ |
| --wrapper->size; \ |
| } \ |
| return front_node; \ |
| } |
| |
| /* Pop the last node of the list. */ |
| #define LINKED_LIST_POP_BACK(LTYPE) LTYPE##_pop_back |
| |
| #define LINKED_LIST_DECL_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_pop_back (LWRAPPERTYPE *wrapper) |
| |
| #define LINKED_LIST_DEFN_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_pop_back (LWRAPPERTYPE *wrapper) \ |
| { \ |
| LTYPE *back_node = wrapper->last; \ |
| if (back_node != NULL) \ |
| { \ |
| wrapper->last = back_node->prev; \ |
| if (wrapper->first == back_node) \ |
| wrapper->first = NULL; \ |
| else \ |
| { \ |
| back_node->prev->next = NULL; \ |
| back_node->prev = NULL; \ |
| } \ |
| back_node->prev = NULL; \ |
| --wrapper->size; \ |
| } \ |
| return back_node; \ |
| } |
| |
| /* Remove the given node from the existing list, and return the previous |
| node. */ |
| #define LINKED_LIST_REMOVE(LTYPE) LTYPE##_remove |
| |
| #define LINKED_LIST_DECL_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_remove (LWRAPPERTYPE *wrapper, LTYPE *node) |
| |
| #define LINKED_LIST_DEFN_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_remove (LWRAPPERTYPE *wrapper, LTYPE *node) \ |
| { \ |
| LTYPE *previous = NULL; \ |
| \ |
| if (node->prev != NULL) \ |
| { \ |
| node->prev->next = node->next; \ |
| if (node->next == NULL) \ |
| wrapper->last = node->prev; \ |
| else \ |
| node->next->prev = node->prev; \ |
| previous = node->prev; \ |
| node->next = NULL; \ |
| node->prev = NULL; \ |
| --wrapper->size; \ |
| } \ |
| else \ |
| LTYPE##_pop_front (wrapper); \ |
| \ |
| return previous; \ |
| } |
| |
| /* Generic swap. */ |
| #define LINKED_LIST_SWAP(LTYPE) LTYPE##_swap |
| |
| #define LINKED_LIST_DECL_SWAP(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_swap (LWRAPPERTYPE *wrapper, LTYPE *node1, LTYPE *node2) |
| |
| /* Swap two nodes in a list. */ |
| #define LINKED_LIST_DEFN_SWAP(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_swap (LWRAPPERTYPE *wrapper, LTYPE *node1, LTYPE *node2) \ |
| { \ |
| LTYPE *prev1 = node1->prev; \ |
| LTYPE *next1 = node1->next; \ |
| LTYPE *prev2 = node2->prev; \ |
| LTYPE *next2 = node2->next; \ |
| \ |
| if (prev1 != NULL) \ |
| prev1->next = node2; \ |
| else \ |
| wrapper->first = node2; \ |
| if (prev2 != NULL) \ |
| prev2->next = node1; \ |
| else \ |
| wrapper->first = node1; \ |
| \ |
| if (next1 != NULL) \ |
| next1->prev = node2; \ |
| else \ |
| wrapper->last = node2; \ |
| if (next2 != NULL) \ |
| next2->prev = node1; \ |
| else \ |
| wrapper->last = node1; \ |
| \ |
| { \ |
| LTYPE *temp = node1->next; \ |
| node1->next = node2->next; \ |
| node2->next = temp; \ |
| } \ |
| { \ |
| LTYPE *temp = node1->prev; \ |
| node1->prev = node2->prev; \ |
| node2->prev = temp; \ |
| } \ |
| } |
| |
| /* Note: all the mutative operations below also update the data in the wrapper, |
| i.e. first, last and size. */ |
| #define LINKED_LIST_MUTATIVE_OPS_PROTOTYPE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DECL_APPEND(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT); \ |
| LINKED_LIST_DECL_SWAP(LWRAPPERTYPE, LTYPE, EXPORT) |
| |
| #define LINKED_LIST_MUTATIVE_OPS_DECL(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_APPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| LINKED_LIST_DEFN_SWAP(LWRAPPERTYPE, LTYPE, EXPORT) |
| |
| |
| /* Sorting. */ |
| |
| #define LINKED_LIST_MERGE_SORT_(LTYPE) LTYPE##_merge_sort_ |
| |
| #define LINKED_LIST_MERGE_SORT(LTYPE) LTYPE##_merge_sort |
| |
| #define LINKED_LIST_MERGE_SORT_PROTOTYPE_(LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_merge_sort_ (LTYPE *node, \ |
| int (*fn_cmp) (const LTYPE *, const LTYPE *)) |
| |
| #define LINKED_LIST_MERGE_SORT_PROTOTYPE(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_merge_sort (LWRAPPERTYPE *wrapper, \ |
| int (*fn_cmp) (const LTYPE *, const LTYPE *)) |
| |
| /* Note: all the functions and macros below starting with "_" should be |
| considered private. */ |
| |
| /* Compute the middle element of the list based on the turtle and hare |
| approach, i.e. the hare runs twice faster than the turtle. */ |
| #define _MERGE_SORT_IMPL_COMPUTE_TURTLE(LTYPE) \ |
| static inline LTYPE * \ |
| LTYPE##_merge_sort_compute_turtle_ (LTYPE *node) \ |
| { \ |
| if (node == NULL) \ |
| return node; \ |
| \ |
| LTYPE *turtle = node, *hare = node->next; \ |
| while (hare != NULL && hare->next != NULL) \ |
| { \ |
| turtle = turtle->next; \ |
| hare = hare->next->next; \ |
| } \ |
| return turtle; \ |
| } |
| |
| /* Append n at the end of l_out, and return the next node after n. |
| l_out and l_last should be ideally encapsulated into a list structure |
| but this is overkill for what we need here. */ |
| #define _MERGE_SORT_IMPL_OUT_APPEND(LTYPE) \ |
| static inline LTYPE * \ |
| LTYPE##_merge_sort_out_append_ (LTYPE **l_out, LTYPE **l_last, \ |
| LTYPE *n) \ |
| { \ |
| if (*l_last == NULL) \ |
| { \ |
| *l_last = n; \ |
| *l_out = n; \ |
| n->prev = NULL; \ |
| } \ |
| else \ |
| { \ |
| (*l_last)->next = n; \ |
| n->prev = *l_last; \ |
| *l_last = n; \ |
| } \ |
| \ |
| return n->next; \ |
| } |
| |
| /* Merge two sorted lists together. |
| The returned value corresponds to the first element of the list. |
| Note: both input lists are invalidated after the call. */ |
| #define _MERGE_SORT_IMPL_MERGE(LTYPE) \ |
| static inline LTYPE * \ |
| LTYPE##_merge_sort_merge_ (LTYPE *l_left, LTYPE *l_right, \ |
| int (*fn_cmp) (const LTYPE *, const LTYPE *))\ |
| { \ |
| if (l_left == NULL) \ |
| return l_right; \ |
| else if (l_right == NULL) \ |
| return l_left; \ |
| \ |
| LTYPE *l_out = NULL, *l_last = NULL; \ |
| \ |
| LTYPE *l_l = l_left, *l_r = l_right; \ |
| while (l_l != NULL && l_r != NULL) \ |
| { \ |
| int cmp = fn_cmp (l_l, l_r); \ |
| if (cmp <= 0) \ |
| l_l = LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_l); \ |
| else \ |
| l_r = LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_r); \ |
| } \ |
| \ |
| LTYPE *l_remaining = (l_l != NULL) ? l_l : l_r; \ |
| while (l_remaining != NULL) \ |
| l_remaining = \ |
| LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_remaining); \ |
| \ |
| return l_out; \ |
| } |
| |
| /* Merge sort implementation taking the first node of the list to sort, |
| and the comparison function. Returns the first node of the sorted list. |
| Note: use this if you don't care about updating the information in the |
| wrapper. */ |
| #define _MERGE_SORT_DEFN_SORT(LTYPE, EXPORT) \ |
| EXPORT LTYPE * \ |
| LTYPE##_merge_sort_ (LTYPE *node, \ |
| int (*fn_cmp)(const LTYPE *, const LTYPE *)) \ |
| { \ |
| if (node == NULL) \ |
| return NULL; \ |
| else if (node->next == NULL) \ |
| return node; \ |
| \ |
| LTYPE *left_end = LTYPE##_merge_sort_compute_turtle_ (node); \ |
| LTYPE *left_begin = node; \ |
| LTYPE *right_begin = left_end->next; \ |
| /* break the list. */ \ |
| left_end->next = NULL; \ |
| right_begin->prev = NULL; \ |
| \ |
| left_begin = LTYPE##_merge_sort_ (left_begin, fn_cmp); \ |
| right_begin = LTYPE##_merge_sort_ (right_begin, fn_cmp); \ |
| return LTYPE##_merge_sort_merge_ (left_begin, right_begin, fn_cmp); \ |
| } |
| |
| /* Merge sort wrapper that the end-user should be using as it updates the |
| first and last metadata of the list in wrapper as well. |
| If the user does not want to pay the cost of the update of the data, |
| it can directly use _##LTYPE##_merge_sort_merge. */ |
| #define _MERGE_SORT_DEFN_WRAPPER_SORT(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| EXPORT void \ |
| LTYPE##_merge_sort (LWRAPPERTYPE *wrapper, \ |
| int (*fn_cmp) (const LTYPE *, const LTYPE *)) \ |
| { \ |
| wrapper->first = LTYPE##_merge_sort_ (wrapper->first, fn_cmp); \ |
| \ |
| if (wrapper->first == NULL || wrapper->first->next == NULL) \ |
| wrapper->last = wrapper->first; \ |
| else \ |
| for (LTYPE *node = wrapper->first; \ |
| node != NULL; \ |
| node = node->next) \ |
| wrapper->last = node; \ |
| } |
| |
| #define LINKED_LIST_MERGE_SORT_DECL(LWRAPPERTYPE, LTYPE, EXPORT) \ |
| _MERGE_SORT_IMPL_COMPUTE_TURTLE(LTYPE) \ |
| _MERGE_SORT_IMPL_OUT_APPEND(LTYPE) \ |
| _MERGE_SORT_IMPL_MERGE(LTYPE) \ |
| _MERGE_SORT_DEFN_SORT(LTYPE, EXPORT) \ |
| _MERGE_SORT_DEFN_WRAPPER_SORT(LWRAPPERTYPE, LTYPE, EXPORT) |
| |
| #endif /* _DOUBLY_LINKED_LIST_H */ |