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/doubly-linked-list.h                  | 440 ++++++++++++++++++
 libiberty/Makefile.in                         |   1 +
 libiberty/testsuite/Makefile.in               |  12 +-
 libiberty/testsuite/test-doubly-linked-list.c | 253 ++++++++++
 4 files changed, 705 insertions(+), 1 deletion(-)
 create mode 100644 include/doubly-linked-list.h
 create mode 100644 libiberty/testsuite/test-doubly-linked-list.c

diff --git a/include/doubly-linked-list.h b/include/doubly-linked-list.h
new file mode 100644
index 00000000000..3b3ce1ee6b9
--- /dev/null
+++ b/include/doubly-linked-list.h
@@ -0,0 +1,440 @@
+/* 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
+
+#include <assert.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.  */
+#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_)                                                  \
+    {                                                                  \
+      new_->prev = wrapper->last_;                                     \
+      wrapper->last_->next = new_;                                     \
+    }                                                                  \
+  else                                                                 \
+    {                                                                  \
+      new_->prev = NULL;                                               \
+      wrapper->first_ = new_;                                          \
+    }                                                                  \
+  wrapper->last_ = new_;                                               \
+  ++wrapper->size;                                                     \
+}
+
+/* Prepend the given node new_ to the exiting list.  */
+#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_ after 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 (front_node->next != NULL)                                    \
+       {                                                               \
+         front_node->next->prev = NULL;                                \
+         front_node->next = NULL;                                      \
+       }                                                               \
+      if (wrapper->last_ == front_node)                                        
\
+       wrapper->last_ = 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 (back_node->prev != NULL)                                     \
+       {                                                               \
+         back_node->prev->next = NULL;                                 \
+         back_node->prev = NULL;                                       \
+       }                                                               \
+      if (wrapper->first_ == back_node)                                        
\
+       wrapper->first_ = 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;                                           \
+      --wrapper->size;                                                 \
+    }                                                                  \
+  else                                                                 \
+    LTYPE##_pop_front (wrapper);                                       \
+                                                                       \
+  node->next = NULL;                                                   \
+  node->prev = NULL;                                                   \
+                                                                       \
+  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)
+
+#define VALUE_SWAP(a, b) \
+  do { typeof(a) temp = a; a = b; b = temp; } while (0)
+
+/* Swap two nodes in a list.  */
+#define LINKED_LIST_DEFN_SWAP(LWRAPPERTYPE, LTYPE, EXPORT)             \
+EXPORT void                                                            \
+LTYPE##_swap (LWRAPPERTYPE *wrapper, LTYPE *node1, LTYPE *node2)       \
+{                                                                      \
+  if (node1->prev != NULL)                                             \
+    node1->prev->next = node2;                                         \
+  if (node2->prev != NULL)                                             \
+    node2->prev->next = node1;                                         \
+  if (node1->next != NULL)                                             \
+    node1->next->prev = node2;                                         \
+  if (node2->next != NULL)                                             \
+    node2->next->prev = node1;                                         \
+                                                                       \
+  VALUE_SWAP (node1->next, node2->next);                               \
+  VALUE_SWAP (node1->prev, node2->prev);                               \
+                                                                       \
+  if (wrapper->first_ == node1)                                                
\
+    wrapper->first_ = node2;                                           \
+  else if (wrapper->first_ == node2)                                   \
+    wrapper->first_ = node1;                                           \
+                                                                       \
+  if (wrapper->last_ == node1)                                         \
+    wrapper->last_ = node2;                                            \
+  else if (wrapper->last_ == node2)                                    \
+    wrapper->last_ = node1;                                            \
+}
+
+/* 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 *))     \
+{                                                                      \
+  assert (fn_cmp != NULL);                                             \
+  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 */
diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in
index ce54d88278d..e4d7d0aafa0 100644
--- a/libiberty/Makefile.in
+++ b/libiberty/Makefile.in
@@ -237,6 +237,7 @@ CONFIGURED_OFILES = ./asprintf.$(objext) ./atexit.$(objext) 
        \
 INSTALLED_HEADERS =                                                     \
        $(INCDIR)/ansidecl.h                                            \
        $(INCDIR)/demangle.h                                            \
+       $(INCDIR)/doubly-linked-list.h                                  \
        $(INCDIR)/dyn-string.h                                          \
        $(INCDIR)/fibheap.h                                             \
        $(INCDIR)/floatformat.h                                         \
diff --git a/libiberty/testsuite/Makefile.in b/libiberty/testsuite/Makefile.in
index 2b0883c7630..ef549ca910a 100644
--- a/libiberty/testsuite/Makefile.in
+++ b/libiberty/testsuite/Makefile.in
@@ -45,7 +45,8 @@ all:
 check: @CHECK@
 
 really-check: check-cplus-dem check-d-demangle check-rust-demangle \
-               check-pexecute check-expandargv check-strtol
+               check-pexecute check-expandargv check-strtol \
+               check-doubly-linked-list
 
 # Run some tests of the demangler.
 check-cplus-dem: test-demangle $(srcdir)/demangle-expected
@@ -69,6 +70,10 @@ check-expandargv: test-expandargv
 check-strtol: test-strtol
        ./test-strtol
 
+# Check the linked list functionality
+check-doubly-linked-list: test-doubly-linked-list
+       ./test-doubly-linked-list
+
 # Run the demangler fuzzer
 fuzz-demangler: demangler-fuzzer
        ./demangler-fuzzer
@@ -90,6 +95,10 @@ test-strtol: $(srcdir)/test-strtol.c ../libiberty.a
        $(TEST_COMPILE) -DHAVE_CONFIG_H -I.. -o test-strtol \
                $(srcdir)/test-strtol.c ../libiberty.a
 
+test-doubly-linked-list: $(srcdir)/test-doubly-linked-list.c
+       $(TEST_COMPILE) -DHAVE_CONFIG_H -I.. -o test-doubly-linked-list \
+               $(srcdir)/test-doubly-linked-list.c
+
 demangler-fuzzer: $(srcdir)/demangler-fuzzer.c ../libiberty.a
        $(TEST_COMPILE) -o demangler-fuzzer \
                $(srcdir)/demangler-fuzzer.c ../libiberty.a
@@ -104,6 +113,7 @@ mostlyclean:
        rm -f test-pexecute
        rm -f test-expandargv
        rm -f test-strtol
+       rm -f test-doubly-linked-list
        rm -f demangler-fuzzer
        rm -f core
 clean: mostlyclean
diff --git a/libiberty/testsuite/test-doubly-linked-list.c 
b/libiberty/testsuite/test-doubly-linked-list.c
new file mode 100644
index 00000000000..15534eebacd
--- /dev/null
+++ b/libiberty/testsuite/test-doubly-linked-list.c
@@ -0,0 +1,253 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "doubly-linked-list.h"
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+/* Implementation */
+
+typedef int T;
+
+typedef struct ListNodeType
+{
+  T value;
+  struct ListNodeType *next;
+  struct ListNodeType *prev;
+} ListNodeType;
+
+ListNodeType * l_new_node (T value)
+{
+  ListNodeType *n = malloc (sizeof (ListNodeType));
+  n->next = NULL;
+  n->prev = NULL;
+  n->value = value;
+  return n;
+}
+
+typedef struct LinkedListWrapperType
+{
+  ListNodeType *first_;
+  ListNodeType *last_;
+  size_t size;
+} LinkedListWrapperType;
+
+int compare_nodes (const ListNodeType *n1, const ListNodeType *n2)
+{
+  if (n1->value == n2->value)
+    return 0;
+  else if (n1->value < n2->value)
+    return -1;
+  else
+    return 1;
+}
+
+LINKED_LIST_MUTATIVE_OPS_PROTOTYPE (LinkedListWrapperType, ListNodeType, 
static);
+LINKED_LIST_MERGE_SORT_PROTOTYPE (LinkedListWrapperType, ListNodeType, static);
+
+LINKED_LIST_MUTATIVE_OPS_DECL (LinkedListWrapperType, ListNodeType, static)
+LINKED_LIST_MERGE_SORT_DECL (LinkedListWrapperType, ListNodeType, static)
+
+ListNodeType * find_last_node (ListNodeType *head)
+{
+  if (head == NULL)
+    return NULL;
+
+  ListNodeType *n = head;
+  while (n->next != NULL)
+    n = n->next;
+
+  return n;
+}
+
+void l_print (ListNodeType *node)
+{
+  for (ListNodeType *l = node; l != NULL; l = l->next)
+    printf ("%d ", l->value);
+  printf ("\n");
+}
+
+void l_reverse_print (ListNodeType *last_node)
+{
+  for (ListNodeType *l = last_node; l != NULL; l = l->prev)
+    printf ("%d ", l->value);
+  printf ("\n");
+}
+
+struct test_data_t
+{
+  T const *content;
+  size_t size;
+};
+
+bool run_test (const struct test_data_t *expect,
+              LinkedListWrapperType *current,
+              bool reversed)
+{
+  ListNodeType *node = (reversed) ? current->last_ : current->first_;
+  bool passed = true;
+  for (int i=0; i<expect->size && node != NULL; ++i)
+    {
+      if (reversed)
+       {
+         if (expect->content[expect->size - 1 - i] != node->value)
+           {
+             printf ("FAIL: mismatching expected (%d) VS current (%d).\n",
+                     expect->content[expect->size - 1 - i], node->value);
+             passed = false;
+           }
+         if (node->prev == NULL && current->first_ != node)
+           {
+             printf ("FAIL: first_ is not matching the first node.\n");
+             passed = false;
+           }
+       }
+      else
+       {
+         if (expect->content[i] != node->value)
+           {
+             printf ("FAIL: mismatching expected (%d) VS current (%d).\n",
+                     expect->content[i], node->value);
+             passed = false;
+           }
+         if (node->next == NULL && current->last_ != node)
+           {
+             printf ("FAIL: last_ is not matching the last node.\n");
+             passed = false;
+           }
+       }
+
+      if (!passed)
+       return false;
+
+      if (reversed)
+       node = node->prev;
+      else
+       node = node->next;
+    }
+
+  if (node != NULL)
+    {
+      printf ("FAIL: the list is longer than expected.\n");
+      passed = false;
+    }
+  if (expect->size != current->size)
+    {
+      printf ("FAIL: size (%ld) is not matching the real size of the list 
(%ld).\n",
+             current->size, expect->size);
+      passed = false;
+    }
+
+  return passed;
+}
+
+bool check(const char *op,
+         const struct test_data_t *expect,
+         LinkedListWrapperType *wrapper)
+{
+  bool success = true;
+  bool res;
+
+  l_print (wrapper->first_);
+  res = run_test (expect, wrapper, false);
+  printf ("%s: test-linked-list::%s: check forward conformity\n",
+         res ? "PASS": "FAIL", op);
+  success &= res;
+
+  l_reverse_print (wrapper->last_);
+  res = run_test (expect, wrapper, true);
+  printf ("%s: test-linked-list::%s: check backward conformity\n",
+         res ? "PASS": "FAIL", op);
+  success &= res;
+
+  return success;
+}
+
+const int EXPECT_0 [] = { 10, 4, 3, 1, 9, 2 };
+const int EXPECT_1 [] = { 1, 2, 3, 4, 9, 10 };
+const int EXPECT_2 [] = { 11, 1, 2, 3, 4, 9, 10 };
+const int EXPECT_3 [] = { 11, 1, 2, 3, 4, 9, 8, 10 };
+const int EXPECT_4 [] = { 11, 2, 3, 4, 9, 8, 10 };
+const int EXPECT_5 [] = { 10, 2, 3, 4, 9, 8, 11 };
+const int EXPECT_6 [] = { 2, 3, 4, 8, 9, 10, 11 };
+const int EXPECT_7 [] = { 3, 4, 8, 9, 10, 11 };
+const int EXPECT_8 [] = { 3, 4, 8, 9, 10 };
+const struct test_data_t test_data[] = {
+  { .content = EXPECT_0, .size = sizeof(EXPECT_0) / sizeof(EXPECT_0[0]) },
+  { .content = EXPECT_1, .size = sizeof(EXPECT_1) / sizeof(EXPECT_1[0]) },
+  { .content = EXPECT_2, .size = sizeof(EXPECT_2) / sizeof(EXPECT_2[0]) },
+  { .content = EXPECT_3, .size = sizeof(EXPECT_3) / sizeof(EXPECT_3[0]) },
+  { .content = EXPECT_4, .size = sizeof(EXPECT_4) / sizeof(EXPECT_4[0]) },
+  { .content = EXPECT_5, .size = sizeof(EXPECT_5) / sizeof(EXPECT_5[0]) },
+  { .content = EXPECT_6, .size = sizeof(EXPECT_6) / sizeof(EXPECT_6[0]) },
+  { .content = EXPECT_7, .size = sizeof(EXPECT_7) / sizeof(EXPECT_7[0]) },
+  { .content = EXPECT_8, .size = sizeof(EXPECT_8) / sizeof(EXPECT_8[0]) },
+};
+
+int main (void)
+{
+  int failures = 0;
+
+  LinkedListWrapperType wrapper = {
+    .first_ = NULL,
+    .last_ = NULL,
+    .size = 0,
+  };
+
+  /* Append nodes.  */
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (10));
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (4));
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (3));
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (1));
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (9));
+  LINKED_LIST_APPEND(ListNodeType) (&wrapper, l_new_node (2));
+
+  failures += ! check ("append", &test_data[0], &wrapper);
+
+  /* Sort nodes (without updating wrapper).  */
+  wrapper.first_ =
+    _LINKED_LIST_MERGE_SORT(ListNodeType) (wrapper.first_, compare_nodes);
+  wrapper.last_ = find_last_node (wrapper.first_);
+
+  failures += ! check ("sort", &test_data[1], &wrapper);
+
+  /* Save a reference to this node for later.  */
+  ListNodeType *n_to_remove = wrapper.first_;
+
+  /* Prepend node.  */
+  LINKED_LIST_PREPEND(ListNodeType) (&wrapper, l_new_node (11));
+  failures += ! check ("prepend", &test_data[2], &wrapper);
+
+  /* Insert node.  */
+  LINKED_LIST_INSERT_BEFORE(ListNodeType) (&wrapper, l_new_node (8), 
wrapper.last_);
+  failures += ! check ("insert_before", &test_data[3], &wrapper);
+
+  /* Remove a node.  */
+  LINKED_LIST_REMOVE(ListNodeType) (&wrapper, n_to_remove);
+  failures += ! check ("remove", &test_data[4], &wrapper);
+
+  /* Swap first and last.  */
+  LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first_, wrapper.last_);
+  failures += ! check ("swap", &test_data[5], &wrapper);
+
+  /* Sort nodes.  */
+  LINKED_LIST_MERGE_SORT(ListNodeType) (&wrapper, compare_nodes);
+  failures += ! check ("sort", &test_data[6], &wrapper);
+
+  /* Pop front.  */
+  LINKED_LIST_POP_FRONT(ListNodeType) (&wrapper);
+  failures += ! check ("pop_front", &test_data[7], &wrapper);
+
+  /* Pop back.  */
+  LINKED_LIST_POP_BACK(ListNodeType) (&wrapper);
+  failures += ! check ("pop_back", &test_data[8], &wrapper);
+
+  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
+}
-- 
2.50.0


Reply via email to