diff options
author | 3gg <3gg@shellblade.net> | 2023-06-12 08:52:15 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2023-06-12 08:52:15 -0700 |
commit | bfabb435e5c5bf313005c4636747fce59eb4ca6f (patch) | |
tree | a32248966dd2cfa851a1bc731c3b240ebfaf9110 | |
parent | 0c1eb2535676a6fb2e1def08f9681b2a8b49f6e4 (diff) |
Add list library.
-rw-r--r-- | list/CMakeLists.txt | 7 | ||||
-rw-r--r-- | list/include/list.h | 118 | ||||
-rw-r--r-- | list/src/list.c | 14 | ||||
-rw-r--r-- | list/test/list_test.c | 76 |
4 files changed, 166 insertions, 49 deletions
diff --git a/list/CMakeLists.txt b/list/CMakeLists.txt index 8caeabc..6e76ec6 100644 --- a/list/CMakeLists.txt +++ b/list/CMakeLists.txt | |||
@@ -4,14 +4,11 @@ project(list) | |||
4 | 4 | ||
5 | # Library | 5 | # Library |
6 | 6 | ||
7 | add_library(list | 7 | add_library(list INTERFACE) |
8 | src/list.c) | ||
9 | 8 | ||
10 | target_include_directories(list PUBLIC | 9 | target_include_directories(list INTERFACE |
11 | include) | 10 | include) |
12 | 11 | ||
13 | target_compile_options(list PRIVATE -Wall -Wextra) | ||
14 | |||
15 | # Test | 12 | # Test |
16 | 13 | ||
17 | add_executable(list_test | 14 | add_executable(list_test |
diff --git a/list/include/list.h b/list/include/list.h index 945de28..facf820 100644 --- a/list/include/list.h +++ b/list/include/list.h | |||
@@ -1,21 +1,111 @@ | |||
1 | /// A doubly linked list. | 1 | /// Doubly-linked list. |
2 | /// | ||
3 | /// This list does not hold user data. Instead, the list can be used as an | ||
4 | /// intrusive list or as part as a more complex data structure. | ||
5 | #pragma once | 2 | #pragma once |
6 | 3 | ||
4 | #include <assert.h> | ||
7 | #include <stddef.h> | 5 | #include <stddef.h> |
6 | #include <stdlib.h> | ||
7 | |||
8 | #define DEF_LIST(type) \ | ||
9 | typedef struct type##_node { \ | ||
10 | type val; \ | ||
11 | struct type##_node* prev; \ | ||
12 | struct type##_node* next; \ | ||
13 | } type##_node; \ | ||
14 | typedef struct type##_list { \ | ||
15 | type##_node* head; \ | ||
16 | } type##_list; | ||
17 | |||
18 | static inline void* alloc(size_t size) { | ||
19 | void* ptr = calloc(1, size); | ||
20 | assert(ptr); | ||
21 | return ptr; | ||
22 | } | ||
23 | |||
24 | /// Create a new empty list. | ||
25 | #define make_list(type) \ | ||
26 | (type##_list) { 0 } | ||
27 | |||
28 | /// Delete the list. | ||
29 | #define del_list(list) \ | ||
30 | for (__typeof__(list.head) node = list.head; node;) { \ | ||
31 | __typeof__(node) cur = node; \ | ||
32 | node = node->next; \ | ||
33 | free(cur); \ | ||
34 | } \ | ||
35 | list.head = 0; | ||
8 | 36 | ||
9 | typedef struct list list; | 37 | /// Prepend a value to the list. |
38 | #define list_push(list, value) \ | ||
39 | { \ | ||
40 | __typeof__(list.head) node = alloc(sizeof(*list.head)); \ | ||
41 | node->val = value; \ | ||
42 | node->next = list.head; \ | ||
43 | if (list.head) { \ | ||
44 | list.head->prev = node; \ | ||
45 | } \ | ||
46 | list.head = node; \ | ||
47 | } | ||
10 | 48 | ||
11 | typedef struct list { | 49 | /// Delete the first occurrence of the value from the list. |
12 | list* prev; | 50 | #define list_remove(list, search_value) \ |
13 | list* next; | 51 | for (__typeof__(list.head) iter = list.head; iter; iter = iter->next) { \ |
14 | } list; | 52 | if (iter->val == (search_value)) { \ |
53 | del_node(list, iter); \ | ||
54 | break; \ | ||
55 | } \ | ||
56 | } | ||
15 | 57 | ||
16 | /// Create a new list from an array of `size` items. | 58 | /// Delete the node that contains the value at the given address from the |
17 | void list_make(list* list, size_t size); | 59 | /// list. |
60 | #define list_remove_ptr(list, value_ptr) \ | ||
61 | for (__typeof__(list.head) iter = list.head; iter; iter = iter->next) { \ | ||
62 | if (&iter->val == value_ptr) { \ | ||
63 | del_node(list, iter); \ | ||
64 | break; \ | ||
65 | } \ | ||
66 | } | ||
18 | 67 | ||
19 | /// Iterates over all the items in the list. | 68 | /// Delete the list node. |
20 | #define list_foreach(LIST, iter) \ | 69 | #define del_node(list, node) \ |
21 | for (struct list* iter = LIST; iter; iter = iter->next) | 70 | { \ |
71 | assert(node); \ | ||
72 | if (node->prev) { \ | ||
73 | node->prev->next = node->next; \ | ||
74 | } \ | ||
75 | if (node->next) { \ | ||
76 | node->next->prev = node->prev; \ | ||
77 | } \ | ||
78 | if (list.head == node) { \ | ||
79 | list.head = 0; \ | ||
80 | } \ | ||
81 | free(node); \ | ||
82 | node = 0; \ | ||
83 | } | ||
84 | |||
85 | /// Iterate over all the items in the list. | ||
86 | /// | ||
87 | /// Use 'value' to refer to the address of the current node's value during | ||
88 | /// iteration. | ||
89 | #define list_foreach(list, body) \ | ||
90 | { \ | ||
91 | __typeof__(list.head) node = list.head; \ | ||
92 | while (node) { \ | ||
93 | const __typeof__(node->val)* value = &node->val; \ | ||
94 | body; \ | ||
95 | node = node->next; \ | ||
96 | } \ | ||
97 | } | ||
98 | |||
99 | /// Iterate over all the items in the list. | ||
100 | /// | ||
101 | /// Use 'value' to refer to the address of the current node's value during | ||
102 | /// iteration. | ||
103 | #define list_foreach_mut(list, body) \ | ||
104 | { \ | ||
105 | __typeof__(list.head) node = list.head; \ | ||
106 | while (node) { \ | ||
107 | __typeof__(node->val)* value = &node->val; \ | ||
108 | body; \ | ||
109 | node = node->next; \ | ||
110 | } \ | ||
111 | } | ||
diff --git a/list/src/list.c b/list/src/list.c deleted file mode 100644 index f5b6507..0000000 --- a/list/src/list.c +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | #include "list.h" | ||
2 | |||
3 | #include <assert.h> | ||
4 | |||
5 | void list_make(list* list, size_t size) { | ||
6 | if (size == 0) { | ||
7 | return; | ||
8 | } | ||
9 | assert(list); | ||
10 | for (size_t i = 0; i < size; ++i) { | ||
11 | list[i].prev = (i == 0 ? 0 : &list[i - 1]); | ||
12 | list[i].next = (i == size - 1 ? 0 : &list[i + 1]); | ||
13 | } | ||
14 | } | ||
diff --git a/list/test/list_test.c b/list/test/list_test.c index a11c713..9ff10c1 100644 --- a/list/test/list_test.c +++ b/list/test/list_test.c | |||
@@ -4,31 +4,75 @@ | |||
4 | 4 | ||
5 | #define TEST_LIST_SIZE 10 | 5 | #define TEST_LIST_SIZE 10 |
6 | 6 | ||
7 | // Create an empty list. | 7 | DEF_LIST(int); |
8 | TEST_CASE(list_create_empty) { list_make(0, 0); } | ||
9 | |||
10 | // Create a list of a given size. | ||
11 | TEST_CASE(list_create) { | ||
12 | struct list list[TEST_LIST_SIZE]; | ||
13 | list_make(list, TEST_LIST_SIZE); | ||
14 | } | ||
15 | 8 | ||
16 | // Iterate over a list. | 9 | // Iterate over a list. |
17 | TEST_CASE(list_traverse) { | 10 | TEST_CASE(list_traverse) { |
18 | int numbers[TEST_LIST_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; | 11 | int_list list = make_list(int); |
19 | 12 | for (int i = 0; i < TEST_LIST_SIZE; ++i) { | |
20 | struct list list[TEST_LIST_SIZE]; | 13 | list_push(list, i + 1); |
21 | list_make(list, TEST_LIST_SIZE); | 14 | } |
22 | 15 | ||
23 | int count = 0; | 16 | int count = 0; |
24 | int sum = 0; | 17 | int sum = 0; |
25 | list_foreach(list, item) { | 18 | list_foreach(list, { |
26 | count++; | 19 | count++; |
27 | sum += numbers[item - list]; | 20 | sum += *value; |
28 | } | 21 | }); |
29 | 22 | ||
30 | TEST_EQUAL(count, TEST_LIST_SIZE); | 23 | TEST_EQUAL(count, TEST_LIST_SIZE); |
31 | TEST_EQUAL(sum, TEST_LIST_SIZE * (TEST_LIST_SIZE + 1) / 2); | 24 | TEST_EQUAL(sum, TEST_LIST_SIZE * (TEST_LIST_SIZE + 1) / 2); |
25 | |||
26 | del_list(list); | ||
27 | } | ||
28 | |||
29 | // Delete by value. | ||
30 | TEST_CASE(list_remove_by_value) { | ||
31 | int_list list = make_list(int); | ||
32 | for (int i = 0; i < TEST_LIST_SIZE; ++i) { | ||
33 | list_push(list, i + 1); | ||
34 | } | ||
35 | |||
36 | list_remove(list, 5); | ||
37 | |||
38 | int count = 0; | ||
39 | int sum = 0; | ||
40 | list_foreach(list, { | ||
41 | count++; | ||
42 | sum += *value; | ||
43 | }); | ||
44 | |||
45 | TEST_EQUAL(count, TEST_LIST_SIZE - 1); | ||
46 | TEST_EQUAL(sum, (TEST_LIST_SIZE * (TEST_LIST_SIZE + 1) / 2) - 5); | ||
47 | |||
48 | del_list(list); | ||
49 | } | ||
50 | |||
51 | // Delete by address. | ||
52 | TEST_CASE(list_remove_by_address) { | ||
53 | const int N = 3; | ||
54 | |||
55 | int* ptrs[3] = {0}; | ||
56 | |||
57 | int_list list = make_list(int); | ||
58 | for (int i = 0; i < N; ++i) { | ||
59 | list_push(list, i + 1); | ||
60 | ptrs[i] = &list.head->val; | ||
61 | } | ||
62 | |||
63 | list_remove_ptr(list, ptrs[1]); // Value 2. | ||
64 | |||
65 | int count = 0; | ||
66 | int sum = 0; | ||
67 | list_foreach(list, { | ||
68 | count++; | ||
69 | sum += *value; | ||
70 | }); | ||
71 | |||
72 | TEST_EQUAL(count, 2); | ||
73 | TEST_EQUAL(sum, 1 + 3); | ||
74 | |||
75 | del_list(list); | ||
32 | } | 76 | } |
33 | 77 | ||
34 | int main() { return 0; } | 78 | int main() { return 0; } |