summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/prelude.mk1
-rw-r--r--src/list.h24
-rw-r--r--src/prelude.h2
-rw-r--r--tests/list.c97
-rw-r--r--tests/main.c1
-rw-r--r--tests/tests.h3
6 files changed, 123 insertions, 5 deletions
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
@@ -324,6 +324,25 @@
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.
*
* @param dest
@@ -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 <tavianator@tavianator.com>
+// 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);