diff -r 3f324ea53152 -r 21840975d541 src/tree.c --- a/src/tree.c Wed Feb 14 22:32:13 2024 +0100 +++ b/src/tree.c Thu Feb 15 21:54:43 2024 +0100 @@ -28,6 +28,8 @@ #include "cx/tree.h" +#include "cx/array_list.h" + #include #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) @@ -85,3 +87,85 @@ tree_prev(node) = NULL; tree_next(node) = NULL; } + +int cx_tree_search( + void const *root, + void const *data, + cx_tree_search_func sfunc, + void **result, + ptrdiff_t loc_children, + ptrdiff_t loc_next +) { + int ret; + *result = NULL; + + // shortcut: compare root before doing anything else + ret = sfunc(root, data); + if (ret < 0) { + return ret; + } else if (ret == 0 || tree_children(root) == NULL) { + *result = (void*)root; + return ret; + } + + // create a working stack + size_t work_cap = 32; + size_t work_size = 0; + void const **work = malloc(sizeof(void*) * work_cap); + #define work_add(node) cx_array_add(&work, &work_size, &work_cap, \ + sizeof(void*), &(node), cx_array_default_reallocator) + + // add the children of root to the working stack + { + void *c = tree_children(root); + while (c != NULL) { + work_add(c); + c = tree_next(c); + } + } + + // remember a candidate for adding the data + // also remember the exact return code from sfunc + void *candidate = NULL; + int ret_candidate = -1; + + // process the working stack + while (work_size > 0) { + // pop element + void const *node = work[--work_size]; + + // apply the search function + ret = sfunc(node, data); + + if (ret == 0) { + // if found, exit the search + *result = (void*) node; + work_size = 0; + break; + } else if (ret > 0) { + // if children might contain the data, add them to the stack + void *c = tree_children(node); + while (c != NULL) { + work_add(c); + c = tree_next(c); + } + + // remember this node in case no child is suitable + if (ret_candidate < 0 || ret < ret_candidate) { + candidate = (void *) node; + ret_candidate = ret; + } + } + } + + // not found, but was there a candidate? + if (ret != 0 && candidate != NULL) { + ret = ret_candidate; + *result = candidate; + } + + // free the working queue and return + #undef workq_add + free(work); + return ret; +}