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 /list | |
| parent | 0c1eb2535676a6fb2e1def08f9681b2a8b49f6e4 (diff) | |
Add list library.
Diffstat (limited to 'list')
| -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; } |
