diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..46fad85 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/libakerror"] + path = deps/libakerror + url = https://source.starfort.tech/andrew/libakerror.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f186f..60c1dc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.10) project(akstdlib LANGUAGES C) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb -pg") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -g -ggdb -pg") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -g -ggdb -pg") + if(TARGET akerror::akerror) message(STATUS "FOUND akerror::akerror") else() @@ -11,9 +15,13 @@ include(CTest) include(GNUInstallDirs) include(CMakePackageConfigHelpers) -if(NOT TARGET akerror::akerror) - find_package(PkgConfig REQUIRED) - find_package(akerror REQUIRED) +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_subdirectory(deps/libakerror EXCLUDE_FROM_ALL) +else() + if(NOT TARGET akerror::akerror) + find_package(PkgConfig REQUIRED) + find_package(akerror REQUIRED) + endif() endif() set(akstdlib_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/akstdlib") diff --git a/deps/libakerror b/deps/libakerror new file mode 160000 index 0000000..4fad0ce --- /dev/null +++ b/deps/libakerror @@ -0,0 +1 @@ +Subproject commit 4fad0cec595cca503ce863af2ded3bb7fe3a81d5 diff --git a/src/stdlib.c b/src/stdlib.c index 9507565..d8ddda8 100644 --- a/src/stdlib.c +++ b/src/stdlib.c @@ -191,7 +191,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_strhash_djb2(char *str, size_t len, uint SUCCEED_RETURN(e); } -akerr_ErrorContext AKERR_NOIGNORE *aksl_list_push(aksl_ListNode *list, aksl_ListNode *obj) +akerr_ErrorContext AKERR_NOIGNORE *aksl_list_append(aksl_ListNode *list, aksl_ListNode *obj) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, list, AKERR_NULLPOINTER, "list"); @@ -199,19 +199,20 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_list_push(aksl_ListNode *list, aksl_List aksl_ListNode *slow = list; aksl_ListNode *fast = list; aksl_ListNode *tail = list; - do { - if ( fast != NULL && fast->next != NULL ) { - fast = fast->next->next; - } + while ( fast != NULL && fast->next != NULL ) { tail = slow; slow = slow->next; - if ( fast != NULL && fast == slow) { - FAIL(e, AKERR_CIRCULAR_REFERENCE, "%p", list); + fast = fast->next->next; + if ( fast == slow) { + FAIL_RETURN(e, AKERR_CIRCULAR_REFERENCE, "%p", list); } - } while ( slow != NULL || (fast != NULL && fast->next != NULL) ); + } + if ( fast != NULL ) { + tail = fast; + } tail->next = obj; obj->next = NULL; - obj->prev = slow; + obj->prev = tail; SUCCEED_RETURN(e); } @@ -320,16 +321,23 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_list_iterate(aksl_ListNode *list, aksl_L FAIL_ZERO_RETURN(e, iter, AKERR_NULLPOINTER, "iter"); aksl_ListNode *slow = list; aksl_ListNode *fast = list; - aksl_ListNode *tail = list; - do { - if ( fast != NULL && fast->next != NULL ) { - fast = fast->next->next; - } - PASS(e, iter(slow, data)); + while ( fast != NULL && fast->next != NULL ) { slow = slow->next; - if ( fast != NULL && fast == slow) { - FAIL(e, AKERR_CIRCULAR_REFERENCE, "%p", list); + fast = fast->next->next; + if ( fast == slow) { + FAIL_RETURN(e, AKERR_CIRCULAR_REFERENCE, "%p", list); } - } while ( slow != NULL || (fast != NULL && fast->next != NULL) ); + } + while ( slow != NULL ) { + ATTEMPT { + CATCH(e, iter(slow, data)); + slow = slow->next; + } CLEANUP { + } PROCESS(e) { + } HANDLE(e, AKERR_ITERATOR_BREAK) { + // This is not an error condition, it's just telling us to stop early + SUCCEED_RETURN(e); + } FINISH(e, true); + } SUCCEED_RETURN(e); } diff --git a/tests/test_linkedlist.c b/tests/test_linkedlist.c index 7cd50da..9383de0 100644 --- a/tests/test_linkedlist.c +++ b/tests/test_linkedlist.c @@ -1,33 +1,74 @@ +#include #include + +// This iterator does nothing but print the node names it is visiting akerr_ErrorContext AKERR_NOIGNORE *myiter(aksl_ListNode *node, void *data) { - PREPARE_ERROR(e); - int count; - FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node"); - FAIL_ZERO_RETURN(e, node->data, AKERR_NULLPOINTER, "node->data"); - // This function doesn't use *data so we don't check it - - PASS(e, aksl_fprintf(&count, stderr, "Visiting node : %s", node->data)); - SUCCEED_RETURN(e); + int count = 0; + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node"); + FAIL_ZERO_RETURN(e, node->data, AKERR_NULLPOINTER, "node->data"); + PASS(e, aksl_fprintf(&count, stderr, "Visiting node : %s\n", node->data)); + SUCCEED_RETURN(e); +} + +// This iterator function exits early once the index in `(int *)data` is reached +akerr_ErrorContext AKERR_NOIGNORE *myiter_earlyhalt(aksl_ListNode *node, void *data) +{ + int *idx; + int count = 0; + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node"); + FAIL_ZERO_RETURN(e, node->data, AKERR_NULLPOINTER, "node->data"); + FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data"); + idx = (int *)data; + if ( *idx == 1 ) { + // This exception is eaten by the iterator, we will never see it + FAIL_RETURN(e, AKERR_ITERATOR_BREAK, "stop"); + } + *idx += 1; + SUCCEED_RETURN(e); } int main(void) { PREPARE_ERROR(e); + int idx = 0; aksl_ListNode mylist; - char *node1str = "Node 1"; aksl_ListNode node1; - char *node2str = "Node 2"; aksl_ListNode node2; - node1->data = (void *)&node1str; - PASS(e, aksl_list_push(&mylist, &node1)); + ATTEMPT { + memset((void *)&mylist, 0x00, sizeof(aksl_ListNode)); + memset((void *)&node1, 0x00, sizeof(aksl_ListNode)); + memset((void *)&node2, 0x00, sizeof(aksl_ListNode)); - node2->data = (void *)&node2str; - PASS(e, aksl_list_push(&mylist, &node2)); - - PASS(e, aksl_list_iterate(&mylist, &myiter, NULL)); - - SUCCEED_RETURN(e); + mylist.data = "Root"; + + node1.data = "Node 1"; + CATCH(e, aksl_list_append(&mylist, &node1)); + + node2.data = "Node 2"; + CATCH(e, aksl_list_append(&mylist, &node2)); + + // Iterate over all nodes in the list using the myiter() function + CATCH(e, aksl_list_iterate(&mylist, &myiter, NULL)); + + // Iterate over up to the first 2 nodes in the list and then exit early + idx = 0; + CATCH(e, aksl_list_iterate(&mylist, &myiter_earlyhalt, &idx)); + CATCH(e, aksl_fprintf(&count, stderr, "Iterator exited early at index %d\n", idx)); + + // Break the list with a circular reference, and iterate it again + node2.next = &mylist; + CATCH(e, aksl_list_iterate(&mylist, &myiter, NULL)); + + } CLEANUP { + } PROCESS(e) { + } HANDLE(e, AKERR_CIRCULAR_REFERENCE) { + fprintf(stderr, "Circular reference error caught\n"); + } FINISH_NORETURN(e); + + return 0; }