From 118a9053f04d0e215bb3fe20562990cc73ae69a2 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 31 May 2024 11:55:50 -0400 Subject: list: New SLIST_SPLICE() macro --- build/prelude.mk | 1 + src/list.h | 24 +++++++++++--- src/prelude.h | 2 +- tests/list.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 1 + tests/tests.h | 3 ++ 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 tests/list.c diff --git a/build/prelude.mk b/build/prelude.mk index d0968ea..38e432c 100644 --- a/build/prelude.mk +++ b/build/prelude.mk @@ -108,6 +108,7 @@ UNIT_OBJS := \ obj/tests/bfstd.o \ obj/tests/bit.o \ obj/tests/ioq.o \ + obj/tests/list.o \ obj/tests/main.o \ obj/tests/sighook.o \ obj/tests/trie.o \ diff --git a/src/list.h b/src/list.h index 61d0e5b..d95483f 100644 --- a/src/list.h +++ b/src/list.h @@ -323,6 +323,25 @@ #define SLIST_PREPEND_(list, item, ...) \ LIST_VOID_(SLIST_INSERT_(list, &(list)->head, item, __VA_ARGS__)) +/** + * Splice a singly-linked list into another. + * + * @param dest + * The destination list. + * @param cursor + * A pointer to the item to splice after, e.g. &list->head or list->tail. + * @param src + * The source list. + */ +#define SLIST_SPLICE(dest, cursor, src) \ + LIST_VOID_(SLIST_SPLICE_((dest), (cursor), (src))) + +#define SLIST_SPLICE_(dest, cursor, src) \ + *src->tail = *cursor, \ + *cursor = src->head, \ + dest->tail = *dest->tail ? src->tail : dest->tail, \ + SLIST_INIT(src) + /** * Add an entire singly-linked list to the tail of another. * @@ -332,10 +351,7 @@ * The source list. */ #define SLIST_EXTEND(dest, src) \ - SLIST_EXTEND_((dest), (src)) - -#define SLIST_EXTEND_(dest, src) \ - (src->head ? (*dest->tail = src->head, dest->tail = src->tail, SLIST_INIT(src)) : (void)0) + SLIST_SPLICE(dest, (dest)->tail, src) /** * Remove an item from a singly-linked list. diff --git a/src/prelude.h b/src/prelude.h index c4edcb2..0944df1 100644 --- a/src/prelude.h +++ b/src/prelude.h @@ -137,7 +137,7 @@ extern const char bfs_version[]; /** * Get the length of an array. */ -#define countof(array) (sizeof(array) / sizeof(0[array])) +#define countof(...) (sizeof(__VA_ARGS__) / sizeof(0[__VA_ARGS__])) /** * False sharing/destructive interference/largest cache line size. diff --git a/tests/list.c b/tests/list.c new file mode 100644 index 0000000..e14570f --- /dev/null +++ b/tests/list.c @@ -0,0 +1,97 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "list.h" + +struct item { + int n; + struct item *next; +}; + +struct list { + struct item *head; + struct item **tail; +}; + +static bool check_list_items(struct list *list, int *array, size_t size) { + struct item **cur = &list->head; + for (size_t i = 0; i < size; ++i) { + if (!bfs_check(*cur != NULL)) { + return false; + } + int n = (*cur)->n; + if (!bfs_check(n == array[i], "%d != %d", n, array[i])) { + return false; + } + cur = &(*cur)->next; + } + + if (!bfs_check(*cur == NULL)) { + return false; + } + if (!bfs_check(list->tail == cur)) { + return false; + } + + return true; +} + +#define ARRAY(...) (int[]){ __VA_ARGS__ }, countof((int[]){ __VA_ARGS__ }) +#define EMPTY() NULL, 0 + +bool check_list(void) { + struct list l1; + SLIST_INIT(&l1); + bfs_verify(check_list_items(&l1, EMPTY())); + + struct list l2; + SLIST_INIT(&l2); + bfs_verify(check_list_items(&l2, EMPTY())); + + SLIST_EXTEND(&l1, &l2); + bfs_verify(check_list_items(&l1, EMPTY())); + + struct item i10 = { .n = 10 }; + SLIST_APPEND(&l1, &i10); + bfs_verify(check_list_items(&l1, ARRAY(10))); + + SLIST_EXTEND(&l1, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10))); + + SLIST_SPLICE(&l1, &l1.head, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10))); + + struct item i20 = { .n = 20 }; + SLIST_PREPEND(&l2, &i20); + bfs_verify(check_list_items(&l2, ARRAY(20))); + + SLIST_EXTEND(&l1, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 20))); + bfs_verify(check_list_items(&l2, EMPTY())); + + struct item i15 = { .n = 15 }; + SLIST_APPEND(&l2, &i15); + SLIST_SPLICE(&l1, &i10.next, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 15, 20))); + bfs_verify(check_list_items(&l2, EMPTY())); + + SLIST_EXTEND(&l1, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 15, 20))); + + SLIST_SPLICE(&l1, &i10.next, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 15, 20))); + + SLIST_SPLICE(&l1, &l1.head, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 15, 20))); + + struct item i11 = { .n = 11 }; + struct item i12 = { .n = 12 }; + SLIST_APPEND(&l2, &i11); + SLIST_APPEND(&l2, &i12); + SLIST_SPLICE(&l1, &l1.head->next, &l2); + bfs_verify(check_list_items(&l1, ARRAY(10, 11, 12, 15, 20))); + + return true; +} diff --git a/tests/main.c b/tests/main.c index aef0583..bef2e37 100644 --- a/tests/main.c +++ b/tests/main.c @@ -111,6 +111,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "bfstd", check_bfstd); run_test(&ctx, "bit", check_bit); run_test(&ctx, "ioq", check_ioq); + run_test(&ctx, "list", check_list); run_test(&ctx, "sighook", check_sighook); run_test(&ctx, "trie", check_trie); run_test(&ctx, "xspawn", check_xspawn); diff --git a/tests/tests.h b/tests/tests.h index de95a49..2958fe1 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -27,6 +27,9 @@ bool check_bit(void); /** I/O queue tests. */ bool check_ioq(void); +/** Linked list tests. */ +bool check_list(void); + /** Signal hook tests. */ bool check_sighook(void); -- cgit v1.2.3