From 45bb5399f08b26ceed39dd1793940eb29b5fd642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 10 Apr 2021 22:21:54 +0200 Subject: [PATCH] Write singly linked list including test cases --- include/linklist-lib/singly-linked-list.h | 81 +++++++ src/singly-linked-list.c | 126 +++++++++++ test/CMakeLists.txt | 4 +- test/src/tests.cpp | 252 ++++++++++++++++++++++ 4 files changed, 461 insertions(+), 2 deletions(-) diff --git a/include/linklist-lib/singly-linked-list.h b/include/linklist-lib/singly-linked-list.h index 068542f..f6ca2ee 100644 --- a/include/linklist-lib/singly-linked-list.h +++ b/include/linklist-lib/singly-linked-list.h @@ -1,4 +1,85 @@ #ifndef _SINGLY_LINKED_LIST_H_ #define _SINGLY_LINKED_LIST_H_ +#include + +typedef struct singly_linked_list SlList; + +struct singly_linked_list { + void *data; /**< @brief Pointer to the data */ + SlList *next; /**< @brief Pointer to next element */ +}; + +#define sl_list_next(sll) ((sll)->next) + +/** + * @brief Append element to list + * @param list List to append to. May be NULL + * @param data data to stor ein the element + * @return New Head of list. Store this. + */ +SlList *sl_list_append(SlList *list, void *data); + +/** + * @brief Prepend an element to list + * @param list List to prepend data. May be NULL + * @param data Data to store + * @return New head of list. Store this + */ +SlList *sl_list_prepend(SlList *list, void *data); + +/** + * @brief Insert element into list at position. + * + * If the position is larger than the list's size, + * the element is appended at the end. + * + * @param list List to insert + * @param position Position (0 based. 0 equals head) + * @param data Data to append + * @return New list head + */ +SlList *sl_list_insert(SlList *list, uint32_t position, void *data); + +/** + * @brief Remove an elemnt from the list + * + * If the elemnt is not found, nothing is removed. + * If multiple elments contain this datum, only the first is rmeoved. + * + * @param list List ot remove from + * @param data Pointer to remove. + * @return New list head + */ +SlList *sl_list_remove(SlList *list, const void *data); + +/** + * @brief Free a SlList + * @param list List to free + * @warning This function does not deallocate the data stored. Use @ref sl_list_free_full for that. + */ +void sl_list_free(SlList *list); + +/** + * @brief Free a list including its data items + * @param list List to fully free + * @param destroy_element Destruction function for the data items + */ +void sl_list_free_full(SlList *list, void (*destroy_element)(void *)); + +/** + * @brief Get Length of list + * @param list List + * @return Length of list + */ +uint32_t sl_list_length(SlList *list); + +/** + * @brief Get nth element in list. + * @param list List + * @param n Position of element + * @return nth Element or NULL in case list is to short. + */ +SlList *sl_list_nth(SlList *list, uint32_t n); + #endif /* _SINGLY_LINKED_LIST_H_ */ diff --git a/src/singly-linked-list.c b/src/singly-linked-list.c index e69de29..959cefb 100644 --- a/src/singly-linked-list.c +++ b/src/singly-linked-list.c @@ -0,0 +1,126 @@ +#include +#include + +static SlList *sl_list_alloc() +{ + return (SlList *)malloc(sizeof(SlList)); +} + +static void sl_list_dealloc(SlList *list) +{ + if (list) + free(list); +} + +SlList *sl_list_append(SlList *list, void *data) +{ + SlList *new_element; + SlList **next_iter; + + /* Allocate new element for data */ + new_element = sl_list_alloc(); + new_element->data = data; + new_element->next = NULL; + + for (next_iter = &list; *next_iter; next_iter = &(*next_iter)->next); + + *next_iter = new_element; + + return list; +} + +SlList *sl_list_prepend(SlList *list, void *data) +{ + SlList *new_element; + + /* Allocate new element for data */ + new_element = sl_list_alloc(); + new_element->data = data; + new_element->next = list; + + return new_element; +} + +SlList *sl_list_insert(SlList *list, uint32_t position, void *data) +{ + uint32_t idx; + SlList *new_element; + SlList **next_iter; + + new_element = sl_list_alloc(); + new_element->data = data; + + idx = 0; + for (next_iter = &list; next_iter; next_iter = &(*next_iter)->next) { + if (idx == position || !*next_iter) { + new_element->next = *next_iter; + *next_iter = new_element; + break; + } + idx++; + } + + return list; +} + +SlList *sl_list_remove(SlList *list, const void *data) +{ + SlList **next_iter; + SlList *tmp; + + for (next_iter = &list; next_iter; next_iter = &(*next_iter)->next) { + if (*next_iter == NULL) + break; + if ((*next_iter)->data == data) { + tmp = (*next_iter)->next; + sl_list_dealloc(*next_iter); + *next_iter = tmp; + break; + } + } + + return list; +} + +void sl_list_free(SlList *list) +{ + sl_list_free_full(list, NULL); +} + +void sl_list_free_full(SlList *list, void (*destroy_element)(void *)) +{ + SlList *next; + + while (list) { + next = list->next; + if (destroy_element) + destroy_element(list->data); + sl_list_dealloc(list); + list = next; + } +} + +uint32_t sl_list_length(SlList *list) +{ + uint32_t count = 0; + + for (; list; list = sl_list_next(list)) + count++; + + return count; +} + +SlList *sl_list_nth(SlList *list, uint32_t n) +{ + uint32_t idx; + SlList *ret = NULL; + + for (idx = 0; list; list = sl_list_next(list), idx++) { + if (n == idx) { + ret = list; + break; + } + } + + return ret; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 341d45c..1d49adf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,9 @@ -project(base64-test) +project(linklist-lib-test) add_custom_target(test "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}" "-r compact" "-s" DEPENDS ${PROJECT_NAME}) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/catch-framework") aux_source_directory("src" TEST_SOURCES) add_executable(${PROJECT_NAME} ${TEST_SOURCES}) -target_link_libraries(${PROJECT_NAME} base64-lib) +target_link_libraries(${PROJECT_NAME} linklist-lib) diff --git a/test/src/tests.cpp b/test/src/tests.cpp index c9de5d9..58997f6 100644 --- a/test/src/tests.cpp +++ b/test/src/tests.cpp @@ -2,3 +2,255 @@ #include #include +extern "C" { +#include +} + +TEST_CASE("Append first element", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (void *)0x1234; + + list = sl_list_append(list, ptr); + + REQUIRE(list->data == ptr); + REQUIRE(list->next == NULL); + + free(list); +} + +TEST_CASE("Append second element", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (void *)0x1234; + void *ptr2 = (void *)0x585796; + + list = sl_list_append(list, ptr); + + REQUIRE(list->data == ptr); + REQUIRE(list->next == NULL); + + list = sl_list_append(list, ptr2); + + REQUIRE(list->data == ptr); + REQUIRE(list->next != NULL); + REQUIRE(list->next->data == ptr2); + + if (list->next) + free(list->next); + if (list) + free(list); +} + +TEST_CASE("Prepend element", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (void *)0x12345; + + list = sl_list_prepend(list, ptr); + REQUIRE(list != NULL); + REQUIRE(list->next == NULL); + REQUIRE(list->data == ptr); + if (list) + free(list); +} + +TEST_CASE("Prepend second element", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (void *)0x12345; + void *ptr2 = (void *)0x1AA45; + SlList *list_backup; + + list = sl_list_prepend(list, ptr); + REQUIRE(list != NULL); + REQUIRE(list->next == NULL); + REQUIRE(list->data == ptr); + list_backup = list; + + list = sl_list_prepend(list, ptr2); + + REQUIRE(list != NULL); + REQUIRE(list->next == list_backup); + REQUIRE(list->data == ptr2); + + if (list->next) + free(list->next); + if (list) + free(list); +} + +TEST_CASE("Insert element empty list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (void *)0xAABB; + + list = sl_list_insert(list, 0, ptr); + REQUIRE(list != NULL); + REQUIRE(list->next == NULL); + REQUIRE(list->data == ptr); +} + +TEST_CASE("Insert element at beginning of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAABB; + void *ptr2 = (void *)0x454; + + list = sl_list_insert(list, 0, ptr); + REQUIRE(list != NULL); + REQUIRE(list->next == NULL); + REQUIRE(list->data == ptr); + + + list = sl_list_insert(list, 0, ptr2); + REQUIRE(list != NULL); + REQUIRE(list->next != NULL); + REQUIRE(list->data == ptr2); + REQUIRE(list->next->data == ptr); + REQUIRE(list->next->next == NULL); +} + + +TEST_CASE("Insert element at second position of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAABB; + void *ptr2 = (void *)0x454; + + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr); + + list = sl_list_insert(list, 1, ptr2); + REQUIRE(list != NULL); + REQUIRE(list->next != NULL); + REQUIRE(list->data == ptr); + REQUIRE(list->next->data == ptr2); + REQUIRE(list->data == ptr); + REQUIRE(list->next->next != NULL); + REQUIRE(list->next->next->data == ptr); + REQUIRE(list->next->next->next == NULL); +} + +TEST_CASE("Insert element at overflow position of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAABB; + void *ptr2 = (void *)0x454; + + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr); + + list = sl_list_insert(list, 300, ptr2); + REQUIRE(list != NULL); + REQUIRE(list->next != NULL); + REQUIRE(list->next->next != NULL); + REQUIRE(list->next->next->data == ptr2); +} + +TEST_CASE("Remove 1st element of empty list", "[SLL]") +{ + SlList *list = NULL; + + list = sl_list_remove(list, (void *)0x0); + REQUIRE(list == NULL); +} + +TEST_CASE("Remove 1st element of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAAB54B; + + list = sl_list_append(list, ptr); + + list = sl_list_remove(list, (void *)0x0); + REQUIRE(list != NULL); + + list = sl_list_remove(list, ptr); + REQUIRE(list == NULL); +} + +TEST_CASE("Remove 2nd element of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAAB54B; + void *ptr2 = (char *)0xAA23B54B; + + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr2); + + list = sl_list_remove(list, (void *)0x0); + REQUIRE(list != NULL); + REQUIRE(list->next != NULL); + + list = sl_list_remove(list, ptr2); + REQUIRE(list != NULL); + REQUIRE(list->next == NULL); +} + +TEST_CASE("Remove 4th element of list", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAAB54B; + void *ptr2 = (char *)0xAA23B54B; + + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr); + list = sl_list_append(list, ptr2); + + list = sl_list_remove(list, (void *)0x0); + REQUIRE(list != NULL); + REQUIRE(list->next != NULL); + + list = sl_list_remove(list, ptr2); + REQUIRE(list != NULL); + REQUIRE(list->next->next->next == NULL); +} + +TEST_CASE("List length", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAAB54B; + uint32_t len; + + len = sl_list_length(list); + REQUIRE(len == 0); + + list = sl_list_append(list, ptr); + len = sl_list_length(list); + REQUIRE(len == 1); + + list = sl_list_append(list, ptr); + len = sl_list_length(list); + REQUIRE(len == 2); + + list = sl_list_prepend(list, ptr); + len = sl_list_length(list); + REQUIRE(len == 3); +} + +TEST_CASE("nth element", "[SLL]") +{ + SlList *list = NULL; + void *ptr = (char *)0xAAB54B; + SlList *nth; + + nth = sl_list_nth(list, 0); + REQUIRE(nth == NULL); + nth = sl_list_nth(list, 1); + REQUIRE(nth == NULL); + + list = sl_list_append(list, ptr); + nth = sl_list_nth(list, 0); + REQUIRE(nth == list); + nth = sl_list_nth(list, 1); + REQUIRE(nth == NULL); + + list = sl_list_append(list, ptr); + nth = sl_list_nth(list, 0); + REQUIRE(nth == list); + nth = sl_list_nth(list, 1); + REQUIRE(nth == list->next); + +}