Matthieu Longo <matthieu.lo...@arm.com> writes:
> 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.

Thanks for the update, LGTM.  OK for trunk (and for binutils).

Richard

> ---
>  include/doubly-linked-list.h                  | 447 ++++++++++++++++++
>  libiberty/Makefile.in                         |   1 +
>  libiberty/testsuite/Makefile.in               |  12 +-
>  libiberty/testsuite/test-doubly-linked-list.c | 269 +++++++++++
>  4 files changed, 728 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..3f5ea2808f9
> --- /dev/null
> +++ b/include/doubly-linked-list.h
> @@ -0,0 +1,447 @@
> +/* 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 */
> 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..1e1fc637653
> --- /dev/null
> +++ b/libiberty/testsuite/test-doubly-linked-list.c
> @@ -0,0 +1,269 @@
> +#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;
> +
> +  printf("\n");
> +
> +  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 [] = { 10, 3, 2, 4, 9, 8, 11 };
> +const int EXPECT_7 [] = { 10, 9, 2, 4, 3, 8, 11 };
> +const int EXPECT_8 [] = { 2, 3, 4, 8, 9, 10, 11 };
> +const int EXPECT_9 [] = { 3, 4, 8, 9, 10, 11 };
> +const int EXPECT_10 [] = { 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]) },
> +  { .content = EXPECT_9, .size = sizeof(EXPECT_9) / sizeof(EXPECT_9[0]) },
> +  { .content = EXPECT_10, .size = sizeof(EXPECT_10) / sizeof(EXPECT_10[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 first and last", &test_data[5], &wrapper);
> +
> +  /* Swap adjacent nodes.  */
> +  LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first->next,
> +                               wrapper.first->next->next);
> +  failures += ! check ("swap adjacent nodes", &test_data[6], &wrapper);
> +
> +  /* Swap non-adjacent nodes, but neither first nor last.  */
> +  LINKED_LIST_SWAP(ListNodeType) (&wrapper, wrapper.first->next,
> +                               wrapper.first->next->next->next->next);
> +  failures += ! check ("swap non-adjacent nodes", &test_data[7], &wrapper);
> +
> +  /* Sort nodes.  */
> +  LINKED_LIST_MERGE_SORT(ListNodeType) (&wrapper, compare_nodes);
> +  failures += ! check ("sort", &test_data[8], &wrapper);
> +
> +  /* Pop front.  */
> +  LINKED_LIST_POP_FRONT(ListNodeType) (&wrapper);
> +  failures += ! check ("pop_front", &test_data[9], &wrapper);
> +
> +  /* Pop back.  */
> +  LINKED_LIST_POP_BACK(ListNodeType) (&wrapper);
> +  failures += ! check ("pop_back", &test_data[10], &wrapper);
> +
> +  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
> +}

Reply via email to