Files
gcc/include/doubly-linked-list.h
Matthieu Longo 0fd98b6f9f libiberty: add routines to handle type-sensitive doubly linked lists
Those methods's implementation is relying on duck-typing at compile
time.
The structure corresponding to the node of a doubly linked list needs
to define attributes 'prev' and 'next' which are pointers on the type
of a node.
The structure wrapping the nodes and others metadata (first, last, size)
needs to define pointers 'first', and 'last' of the node's type, and
an integer type for 'size'.

Mutative methods can be bundled together and be declarable once via a
same macro, or can be declared separately. The merge sort is bundled
separately.
There are 3 types of macros:
1. for the declaration of prototypes: to use in a header file for a
   public declaration, or as a forward declaration in the source file
   for private declaration.
2. for the declaration of the implementation: to use always in a
   source file.
3. for the invocation of the functions.

The methods can be declared either public or private via the second
argument of the declaration macros.

List of currently implemented methods:
- LINKED_LIST_*:
    - APPEND: insert a node at the end of the list.
    - PREPEND: insert a node at the beginning of the list.
    - INSERT_BEFORE: insert a node before the given node.
    - POP_FRONT: remove the first node of the list.
    - POP_BACK: remove the last node of the list.
    - REMOVE: remove the given node from the list.
    - SWAP: swap the two given nodes in the list.
- LINKED_LIST_MERGE_SORT: a merge sort implementation.

include/ChangeLog:

	* doubly-linked-list.h: New file.

libiberty/ChangeLog:

	* Makefile.in: Add new header.
	* testsuite/Makefile.in: Add new test.
	* testsuite/test-doubly-linked-list.c: New test.
2025-07-09 16:15:55 +01:00

448 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 */