diff --git a/CMakeLists.txt b/CMakeLists.txt index 34e9606..b8c6fac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(libds) set_property(GLOBAL PROPERTY C_STANDARD 90) ADD_COMPILE_OPTIONS(-pedantic -Wall) -add_library(ds STATIC src/libds.c src/vec.c src/ht.c) +add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c) add_executable(libds src/main.c) target_include_directories(ds PUBLIC include) diff --git a/include/ll.h b/include/ll.h new file mode 100644 index 0000000..fe27590 --- /dev/null +++ b/include/ll.h @@ -0,0 +1,113 @@ +#ifndef LIBDS_LL_HEADER +#define LIBDS_LL_HEADER + +#include "libds.h" + +/** + * A simple linked list node, doubly-linked. + */ +struct ll_node_s { + struct ll_node_s* next; + struct ll_node_s* prev; + void* data; +}; + +/** + * A linked list struct, containing pointers to the head and tail node. + */ +struct ll_s { + struct ll_node_s* head; + struct ll_node_s* tail; +}; + +typedef struct ll_node_s ll_node; +typedef struct ll_s ll; + +/** + * Initializes the list with default values. + * @param ll the linked list to initialize. + */ +void ll_init(ll* ll); +/** + * Frees the linked list and all underlying data. + * The stored data is left intact. Use ll_foreach to free + * the data if it is no longer used. + * @param ll + */ +void ll_free(ll* ll); + +/** + * Adds an element to the end of the linked list. + * @param ll the linked list to add to + * @param data the data to add + * @return LIBDS_SUCCESS if all goes well, LIBDS_MALLOC if an allocation fails. + */ +libds_result ll_append(ll* ll, void* data); +/** + * Adds an element to the beginning of the linked list. + * @param ll the linked list to add to + * @param data the data to add + * @return LIBDS_SUCCESS if all goes well, LIBDS_MALLOC if an allocation fails. + */ +libds_result ll_prepend(ll* ll, void* data); +/** + * Removes an element from the list. + * @param ll the linked list from which to remove. + * @param data the data to remove. + */ +void ll_remove(ll* ll, void* data); + +/** + * Runs through every element in the linked list, and compares it against the + * given data using the given comparison function. If the comparison function returns + * true, returns the element that was passed to it. If the comparison function returns + * true for no element, returns NULL. + * @param ll the linked list to iterate through. + * @param data the data to compare elements against + * @param compare the comparison function + * @return the first element that is matched by the comparison function, or NULL if none are matched. + */ +void* ll_find(ll* ll, void* data, compare_func compare); +/** + * Runs through every element in the linked list, and compares it against the + * given data using the given comparison function. If the comparison function returns + * true, calls the foreach function, passing it the element and the variable argument list. + * Stops if any foreach function returns a nonzero code. + * @param ll the linked list to perform the operation on + * @param data the data to pass the comparison function + * @param compare the comparison function operating on the data and each element in the list. + * @param foreach the function to be called on every element recognized by the comparison function + * @param ... variable arguments to be passed on to the foreach function + * @return 0 if all goes well, or the first nonzero code returned by foreach. + */ +int ll_foreach(ll* ll, void* data, compare_func compare, foreach_func foreach, ...); + +/** + * Gets the element at the beginning of the linked list. + * @param ll the linked list to get the data from. + * @return the element at the beginning of the element, or NULL if there is none. + */ +void* ll_head(ll* ll); +/** +* Gets the element at the end of the linked list. +* @param ll the linked list to get the data from. +* @return the element at the end of the element, or NULL if there is none. +*/ +void* ll_tail(ll* ll); +/** + * Removes the element at the beginning of the linked list, + * and returns it. + * @param ll the linked list to perform the operation on. + * @return the value removed from the beginning of the linked list, or NULL if there was none. + */ +void* ll_pophead(ll* ll); +/** + * Removes the element at the end of the linked list, + * and returns it. + * @param ll the linked list to perform the operation on. + * @return the value removed from the end of the linked list, or NULL if there was none. + */ +void* ll_poptail(ll* ll); + + +#endif diff --git a/src/ll.c b/src/ll.c new file mode 100644 index 0000000..48e3def --- /dev/null +++ b/src/ll.c @@ -0,0 +1,114 @@ +#include "ll.h" +#include + +void ll_init(ll* ll) { + ll->head = NULL; + ll->tail = NULL; +} +void ll_free(ll* ll) { + ll_node* head = ll->head; + while (head) { + ll_node* to_free = head; + head = head->next; + free(to_free); + } + ll->tail = NULL; + ll->head = NULL; +} + +libds_result ll_append(ll* ll, void* data) { + libds_result result = LIBDS_SUCCESS; + ll_node** to_set = ll->head ? &ll->tail->next : &ll->head; + ll_node* new_node = malloc(sizeof(*new_node)); + if (new_node) { + new_node->next = NULL; + new_node->prev = ll->tail; + new_node->data = data; + + *to_set = new_node; + ll->tail = new_node; + } else { + result = LIBDS_MALLOC; + } + return result; +} +libds_result ll_prepend(ll* ll, void* data) { + libds_result result = LIBDS_SUCCESS; + ll_node** to_set = ll->tail ? &ll->head->prev : &ll->tail; + ll_node* new_node = malloc(sizeof(*new_node)); + if (new_node) { + new_node->next = ll->head; + new_node->prev = NULL; + new_node->data = data; + + *to_set = new_node; + ll->head = new_node; + } + return result; +} +void ll_remove(ll* ll, void* data) { + ll_node* head = ll->head; + while (head) { + if (head->data == data) { + ll_node* to_delete = head; + + head = head->next; + *(to_delete->prev ? &to_delete->prev->next : &ll->head) = to_delete->next; + *(to_delete->next ? &to_delete->next->prev : &ll->tail) = to_delete->prev; + free(to_delete); + } else { + head = head->next; + } + } +} + +void* ll_find(ll* ll, void* data, compare_func compare) { + void* to_return = NULL; + ll_node* head = ll->head; + while (head && to_return == NULL) { + if (compare(data, head->data)) { + to_return = head->data; + } + } + return to_return; +} +int ll_foreach(ll* ll, void* data, compare_func compare, foreach_func foreach, + ...) { + int return_code = 0; + ll_node* head = ll->head; + va_list args; + while (head && return_code == 0) { + if (compare(data, head->data)) { + va_start(args, foreach); + return_code = foreach (head->data, args); + va_end(args); + } + head = head->next; + } + return return_code; +} + +void* ll_head(ll* ll) { return ll->head ? ll->head->data : NULL; } +void* ll_tail(ll* ll) { return ll->tail ? ll->tail->data : NULL; } +void* ll_pophead(ll* ll) { + void* to_return = NULL; + if (ll->head) { + ll_node* to_delete = ll->head; + to_return = to_delete->data; + *(to_delete->next ? &to_delete->next->prev : &ll->tail) = NULL; + ll->head = to_delete->next; + free(to_delete); + } + return to_return; +} +void* ll_poptail(ll* ll) { + void* to_return = NULL; + if (ll->tail) { + ll_node* to_delete = ll->tail; + to_return = to_delete->data; + *(to_delete->prev ? &to_delete->prev->next : &ll->head) = NULL; + ll->tail = to_delete->prev; + free(to_delete); + } + return to_return; +} diff --git a/src/main.c b/src/main.c index a391cc6..9871cba 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include #include #include "ht.h" +#include "ll.h" #include "vec.h" int _test_vec_foreach_func(void* data, va_list args) { @@ -145,9 +146,9 @@ int test_ht_foreach() { int return_code; int sum = 0; ht test_ht; - ht_init(&test_ht); char* test_one = "one"; char* test_two = "two"; + ht_init(&test_ht); ht_put(&test_ht, test_one, test_two); ht_put(&test_ht, test_two, test_one); ht_foreach(&test_ht, NULL, compare_always, _test_ht_foreach_func, &sum); @@ -156,6 +157,101 @@ int test_ht_foreach() { return return_code; } +int _test_ll_foreach_count(void* data, va_list args){ + (*va_arg(args, int*))++; + return 0; +} + +int test_ll_basic() { + ll test_ll; + ll_init(&test_ll); + ll_free(&test_ll); + return 1; +} +int test_ll_append() { + int return_code; + ll test_ll; + char* test_string = "test"; + ll_init(&test_ll); + ll_append(&test_ll, test_string); + return_code = test_ll.head->data == test_string && test_ll.tail->data == test_string; + ll_free(&test_ll); + return return_code; +} +int test_ll_prepend() { + int return_code; + ll test_ll; + char* test_string = "test"; + ll_init(&test_ll); + ll_prepend(&test_ll, test_string); + return_code = test_ll.head->data == test_string && test_ll.tail->data == test_string; + ll_free(&test_ll); + return return_code; +} +int test_ll_remove() { + int return_code; + ll test_ll; + char* test_string = "test"; + ll_init(&test_ll); + ll_prepend(&test_ll, test_string); + ll_prepend(&test_ll, test_string); + ll_remove(&test_ll, test_string); + return_code = test_ll.tail == NULL && test_ll.head == NULL; + ll_free(&test_ll); + return return_code; +} +int test_ll_find() { + int return_code; + ll test_ll; + char* test_string = "test"; + ll_init(&test_ll); + ll_prepend(&test_ll, test_string); + return_code = ll_find(&test_ll, "test", _test_vec_find_string) == test_string; + ll_free(&test_ll); + return return_code; +} +int test_ll_foreach(){ + int return_code; + int count = 0; + ll test_ll; + char* test_string = "test"; + ll_init(&test_ll); + ll_append(&test_ll, test_string); + ll_append(&test_ll, test_string); + ll_foreach(&test_ll, NULL, compare_always, _test_ll_foreach_count, &count); + return_code = count == 2; + ll_free(&test_ll); + return return_code; +} +int test_ll_pophead(){ + int return_code; + ll test_ll; + char* test_string_one = "test 1"; + char* test_string_two = "test 2"; + ll_init(&test_ll); + ll_append(&test_ll, test_string_one); + ll_append(&test_ll, test_string_two); + return_code = ll_pophead(&test_ll) == test_string_one + && ll_head(&test_ll) == ll_tail(&test_ll) + && ll_head(&test_ll) == test_string_two; + ll_free(&test_ll); + return return_code; +} +int test_ll_poptail(){ + int return_code; + ll test_ll; + char* test_string_one = "test 1"; + char* test_string_two = "test 2"; + ll_init(&test_ll); + ll_append(&test_ll, test_string_one); + ll_append(&test_ll, test_string_two); + return_code = ll_poptail(&test_ll) == test_string_two + && ll_head(&test_ll) == ll_tail(&test_ll) + && ll_head(&test_ll) == test_string_one; + ll_free(&test_ll); + return return_code; +} + int run_test(char* test_name, int (*test_func)()) { int success = test_func(); printf("Running test %-15s . . . ", test_name); @@ -164,18 +260,18 @@ int run_test(char* test_name, int (*test_func)()) { } int main(int argc, char** argv) { - char* test_names[11] = {"vec_basic", "vec_add", "vec_remove", "vec_find", + char* test_names[19] = {"vec_basic", "vec_add", "vec_remove", "vec_find", "vec_foreach", "vec_index", "ht_basic", "ht_put", - "ht_get", "ht_remove", "ht_foreach"}; + "ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail"}; - int (*test_functions[11])() = { + int (*test_functions[19])() = { test_vec_basic, test_vec_add, test_vec_remove, test_vec_find, test_vec_foreach, test_vec_index, test_ht_basic, test_ht_put, - test_ht_get, test_ht_remove, test_ht_foreach}; + test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail}; int test_index = 0; int result = 1; - for (; test_index < 11 && result; test_index++) { + for (; test_index < 19 && result; test_index++) { result = run_test(test_names[test_index], test_functions[test_index]); } return result ? EXIT_SUCCESS : EXIT_FAILURE;