diff --git a/inc/cex/containers/linked_list.h b/inc/cex/containers/linked_list.h new file mode 100644 index 0000000..ede282d --- /dev/null +++ b/inc/cex/containers/linked_list.h @@ -0,0 +1,55 @@ +#ifndef CEX_CONTAINERS_linked_list_t_H +#define CEX_CONTAINERS_linked_list_t_H + +#include +#include +#include + +typedef struct linked_list_node_t { + struct linked_list_node_t* prev; + struct linked_list_node_t* next; + + void* data; +} linked_list_node_t; + +typedef struct { + linked_list_node_t* first; + size_t length; + size_t data_size; +} linked_list_t; + +typedef int64_t index_t; + +typedef void (*list_iterator_t)(index_t index, void* data, void* arg); +typedef bool (*list_filter_t)(index_t index, const void* data, void* arg); + +linked_list_t LL_create(size_t data_size); +linked_list_t LL_from_array(size_t data_size, void* array, index_t length); +linked_list_t LL_copy(linked_list_t* list); + +void* LL_to_array(linked_list_t* list); + +void LL_destroy(linked_list_t* list); + +void* LL_insert(linked_list_t* list, index_t index); +void* LL_insert_value(linked_list_t* list, index_t index, const void* data); + +void* LL_add(linked_list_t* list); +void* LL_add_value(linked_list_t* list, const void* data); + +void LL_remove(linked_list_t* list, index_t index); + +void* LL_get(linked_list_t* list, index_t index); +void LL_set(linked_list_t* list, index_t index, const void* data); + +void LL_iterate(linked_list_t* list, list_iterator_t func, void* arg); +void LL_iterate_range(linked_list_t* list, list_iterator_t func, void* arg, index_t from, index_t to); + +void LL_ITER_COPY(index_t _, void* data, void* list); +void LL_ITER_SUM(index_t _, void* data, void* result); +void LL_ITER_PRINT(index_t i, void* data, void* _); + +linked_list_t LL_filter(linked_list_t* list, list_filter_t func, void* arg); +linked_list_t LL_filter_range(linked_list_t* list, list_filter_t func, void* arg, index_t from, index_t to); + +#endif \ No newline at end of file diff --git a/src/cex/containers/linked_list.c b/src/cex/containers/linked_list.c new file mode 100644 index 0000000..e05fce2 --- /dev/null +++ b/src/cex/containers/linked_list.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +linked_list_node_t* step(linked_list_node_t* node, index_t amount, size_t length) { + amount %= length; + + if (amount > 0) { + while (amount != 0) { + amount--; + node = node->next; + } + } else { + while (amount != 0) { + amount++; + node = node->prev; + } + } + + return node; +} + +linked_list_t LL_create(size_t data_size) { + linked_list_t list = {.data_size = data_size, .first = NULL, .length = 0}; + return list; +} + +linked_list_t LL_from_array(size_t data_size, void* array, index_t length) { + linked_list_t list = LL_create(data_size); + + for (index_t i = 0; i < length; i++) { + LL_add_value(&list, array + (data_size * i)); + } + + return list; +} + +void* LL_to_array(linked_list_t* list) { + void* array = malloc(list->data_size * list->length); + + linked_list_node_t* node = list->first; + + for (index_t i = 0; i < list->length; i++) { + memcpy(array + (i * list->data_size), node->data, list->data_size); + node = node->next; + } + + return array; +} + +linked_list_t LL_copy(linked_list_t* list) { + linked_list_t result = LL_create(list->data_size); + LL_iterate(list, LL_ITER_COPY, &result); + return result; +} + +void LL_destroy(linked_list_t* list) { + linked_list_node_t* node = list->first->prev; + list->first->prev = NULL; + + while (node != NULL) { + if (node->data != NULL) + free(node->data); + + linked_list_node_t* follwing = node->prev; + free(node); + node = follwing; + } + + list->first = NULL; + list->length = 0; +} + +void* LL_add(linked_list_t* list) { + return LL_insert(list, list->length); +} + +void* LL_add_value(linked_list_t* list, const void* data) { + return LL_insert_value(list, list->length, data); +} + +void* LL_insert(linked_list_t* list, index_t index) { + linked_list_node_t* node = (linked_list_node_t*)malloc(sizeof(linked_list_node_t)); + + if (list->data_size != 0) + node->data = malloc(list->data_size); + else + node->data = NULL; + + linked_list_node_t* prev; + linked_list_node_t* next; + + if (list->length == 0) { + prev = node; + next = node; + list->first = node; + } else if (index == 0) { + prev = list->first->prev; + next = list->first; + list->first = node; + } else { + index = index > 0 ? index - 1 : index; + prev = step(list->first, index, list->length); + next = prev->next; + } + + // prev -> node -> next + prev->next = node; + node->prev = prev; + next->prev = node; + node->next = next; + + list->length++; + + return node->data; +} + +void* LL_insert_value(linked_list_t* list, index_t index, const void* data) { + void* node_data = LL_insert(list, index); + memcpy(node_data, data, list->data_size); + return node_data; +} + +void LL_remove(linked_list_t* list, index_t index) { + if (list->length == 1) { + if (list->first->data != NULL) + free(list->first->data); + free(list->first); + list->length--; + + return; + } + + linked_list_node_t* node = step(list->first, index, list->length); + node->prev = node->next; + node->next = node->prev; + + if (node->data != NULL) + free(node->data); + free(node); +} + +void* LL_get(linked_list_t* list, index_t index) { + return step(list->first, index, list->length)->data; +} + +void LL_set(linked_list_t* list, index_t index, const void* data) { + memcpy(step(list->first, index, list->length)->data, data, list->data_size); +} + +void LL_iterate_range(linked_list_t* list, list_iterator_t func, void* arg, index_t from, index_t to) { + linked_list_node_t* node = step(list->first, from, list->length); + + for (index_t i = from; i < to; i++) { + func(i, node->data, arg); + node = node->next; + } +} + +void LL_iterate(linked_list_t* list, list_iterator_t func, void* arg) { + LL_iterate_range(list, func, arg, 0, list->length); +} + +linked_list_t LL_filter_range(linked_list_t* list, list_filter_t func, void* arg, index_t from, index_t to) { + linked_list_node_t* node = step(list->first, from, list->length); + + linked_list_t result = LL_create(list->data_size); + + for (index_t i = from; i < to; i++) { + if (func(i, node->data, arg)) { + LL_add_value(&result, node->data); + } + node = node->next; + } + + return result; +} + +linked_list_t LL_filter(linked_list_t* list, list_filter_t func, void* arg) { + return LL_filter_range(list, func, arg, 0, list->length); +} + +void LL_ITER_COPY(index_t _, void* data, void* list) { + LL_add_value((linked_list_t*)list, data); +} + +void LL_ITER_SUM(index_t _, void* data, void* result) { + *(int64_t*)result += *(int64_t*)data; +} + +void LL_ITER_PRINT(index_t i, void* data, void* _) { + printf("#%ld: %ld\n", i, *(int64_t*)data); +} \ No newline at end of file diff --git a/test/inc/cex/containers/test_linked_list.h b/test/inc/cex/containers/test_linked_list.h new file mode 100644 index 0000000..391b24d --- /dev/null +++ b/test/inc/cex/containers/test_linked_list.h @@ -0,0 +1,4 @@ +#include +#include + +test_result_t test_linked_list(); \ No newline at end of file diff --git a/test/src/cex/containers/test_linked_list.c b/test/src/cex/containers/test_linked_list.c new file mode 100644 index 0000000..61a5da3 --- /dev/null +++ b/test/src/cex/containers/test_linked_list.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +TEST(linked_list) + +LABEL("create") +linked_list_t list = LL_create(sizeof(uint64_t)); +ASSERT_EQ("data size set", list.data_size, sizeof(uint64_t)) +ASSERT_EQ("length = 0", list.length, 0) +ASSERT_EQ("first = NULL", list.first, NULL) + +LABEL("add") +int64_t num = 69; +LL_add_value(&list, &num); +ASSERT_EQ("length = 1", list.length, 1) +ASSERT_NEQ("first = NULL", list.first, NULL) +ASSERT_EQ("next = first", list.first->next, list.first) +ASSERT_EQ("prev = first", list.first->prev, list.first) +ASSERT_EQ("*data = num", *(int64_t*)list.first->data, num) + +LABEL("destroy") +LL_destroy(&list); +ASSERT_EQ("length = 0", list.length, 0) +ASSERT_EQ("first = NULL", list.first, NULL) + +LABEL("from array") +uint64_t array[10] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; +list = LL_from_array(sizeof(uint64_t), array, 10); + +LABEL("iterate") +LL_iterate(&list, LL_ITER_PRINT, NULL); + +LABEL("to array") +uint64_t* result_array = LL_to_array(&list); +for (index_t i = 0; i < 10; i++) { + ASSERT_EQ("=", array[i], result_array[i]); +} +free(result_array); + +LL_destroy(&list); + +END_TEST \ No newline at end of file diff --git a/test/src/main.c b/test/src/main.c new file mode 100644 index 0000000..3d7c212 --- /dev/null +++ b/test/src/main.c @@ -0,0 +1,6 @@ +#include +#include + +PROJECT("cex") +RUN_TEST(linked_list) +END_PROJECT