Files
gcc/libiberty/testsuite/test-doubly-linked-list.c
Matthieu Longo 7722c043f0 libiberty: add helper to swap doubly-linked list wrappers
This patch introduces a new helper to swap the contents of two
doubly-linked list wrappers. The new *_swap_lists operation exchanges
the first, last, and size fields, allowing two lists to be swapped
efficiently without iterating over their elements.

This helper is intended for cases where the ownership of a list must be
exchanged but swapping wrapper pointers is not possible. For simple
references to lists, when wrappers are dynamically allocated, swapping
the wrapper pointers themselves is sufficient and remains the preferred
approach.

This change adds the necessary declaration and definition macros to
doubly-linked-list.h and integrates them into the set of mutative
list operations.

The testsuite is updated accordingly to cover the new functionality.

include/ChangeLog:

	* doubly-linked-list.h
	(LINKED_LIST_SWAP_LISTS): Add new helper.
	(LINKED_LIST_DECL_SWAP_LISTS): Likewise.
	(LINKED_LIST_DEFN_SWAP_LISTS): Likewise.
	(LWRAPPERTYPE##_swap_lists): Likewise.

libiberty/ChangeLog:

	* testsuite/test-doubly-linked-list.c: Update.
2026-02-05 15:35:03 +00:00

299 lines
8.4 KiB
C

#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;
#define DUMP_LIST 0
if (DUMP_LIST)
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;
if (DUMP_LIST)
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;
if (DUMP_LIST)
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 int EXPECT_11 [] = { 20, 21, 22 };
const int EXPECT_12 [] = { 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]) },
{ .content = EXPECT_11, .size = sizeof(EXPECT_11) / sizeof(EXPECT_11[0]) },
{ .content = EXPECT_12, .size = sizeof(EXPECT_12) / sizeof(EXPECT_12[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);
LinkedListWrapperType other_wrapper = {
.first = NULL,
.last = NULL,
.size = 0,
};
LINKED_LIST_APPEND(ListNodeType) (&other_wrapper, l_new_node (20));
LINKED_LIST_APPEND(ListNodeType) (&other_wrapper, l_new_node (21));
LINKED_LIST_APPEND(ListNodeType) (&other_wrapper, l_new_node (22));
/* Swap wrappers once. */
LINKED_LIST_SWAP_LISTS (LinkedListWrapperType) (&wrapper, &other_wrapper);
failures += ! check ("swap wrappers once", &test_data[11], &wrapper);
/* Swap wrappers twice. */
LINKED_LIST_SWAP_LISTS (LinkedListWrapperType) (&wrapper, &other_wrapper);
failures += ! check ("swap wrappers twice", &test_data[12], &wrapper);
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
}