diff --git a/Tests/LibC/TestSearch.cpp b/Tests/LibC/TestSearch.cpp index 170f1345f0..892295d42c 100644 --- a/Tests/LibC/TestSearch.cpp +++ b/Tests/LibC/TestSearch.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -13,6 +14,17 @@ #define NODE(node) static_cast(node) #define ROOTP(root) reinterpret_cast(root) #define COMP(func) reinterpret_cast(func) +#define U8(value) static_cast(value) + +struct twalk_test_entry { + const void* node; + VISIT order; + int depth; +}; + +#define TWALK_SET_DATA (-2) +#define TWALK_CHECK_END (-3) +#define TWALK_END_MARKER (-4) TEST_CASE(tsearch) { @@ -137,3 +149,104 @@ TEST_CASE(tfind) delete_node_recursive(root); } + +void twalk_action(const void* node, VISIT order, int depth); +void twalk_action(const void* node, VISIT order, int depth) +{ + static int count = 0; + static const struct twalk_test_entry* tests = nullptr; + + // Special case: Set test data. + if (depth == TWALK_SET_DATA) { + count = 0; + tests = static_cast(node); + return; + } + + // Special case: End signaled by tester. + if (depth == TWALK_CHECK_END) { + if (tests[count].depth != TWALK_END_MARKER) { + FAIL(String::formatted("Expected action (node={:#x}, order={}, depth={}), but twalk ended early.", + tests[count].node, U8(tests[count].order), tests[count].depth)); + } + return; + } + + // Special case: End marker reached. + if (tests[count].depth == TWALK_END_MARKER) { + FAIL(String::formatted("Expected end, but twalk sent another action (node={:#x}, order={}, depth={}).", + node, U8(order), depth)); + return; + } + + EXPECT_EQ(node, tests[count].node); + EXPECT_EQ(U8(order), U8(tests[count].order)); + EXPECT_EQ(depth, tests[count].depth); + + count++; +} + +TEST_CASE(twalk) +{ + struct search_tree_node* root = nullptr; + + // Try an empty tree. + struct twalk_test_entry tests1[] = { + { nullptr, leaf, TWALK_END_MARKER }, + }; + twalk_action(tests1, leaf, TWALK_SET_DATA); + twalk(nullptr, twalk_action); + twalk_action(nullptr, leaf, TWALK_CHECK_END); + + // Try a single node. + root = new_tree_node("5"); + struct twalk_test_entry tests2[] = { + { root, leaf, 0 }, + { nullptr, leaf, TWALK_END_MARKER }, + }; + twalk_action(tests2, leaf, TWALK_SET_DATA); + twalk(root, twalk_action); + twalk_action(nullptr, leaf, TWALK_CHECK_END); + + // Try two layers of nodes. + root->left = new_tree_node("3"); + root->right = new_tree_node("7"); + struct twalk_test_entry tests3[] = { + { root, preorder, 0 }, + { root->left, leaf, 1 }, + { root, postorder, 0 }, + { root->right, leaf, 1 }, + { root, endorder, 0 }, + { nullptr, leaf, TWALK_END_MARKER }, + }; + twalk_action(tests3, leaf, TWALK_SET_DATA); + twalk(root, twalk_action); + twalk_action(nullptr, leaf, TWALK_CHECK_END); + + // Try three layers of nodes. + root->left->left = new_tree_node("2"); + root->left->right = new_tree_node("4"); + root->right->left = new_tree_node("6"); + root->right->right = new_tree_node("8"); + struct twalk_test_entry tests4[] = { + { root, preorder, 0 }, + { root->left, preorder, 1 }, + { root->left->left, leaf, 2 }, + { root->left, postorder, 1 }, + { root->left->right, leaf, 2 }, + { root->left, endorder, 1 }, + { root, postorder, 0 }, + { root->right, preorder, 1 }, + { root->right->left, leaf, 2 }, + { root->right, postorder, 1 }, + { root->right->right, leaf, 2 }, + { root->right, endorder, 1 }, + { root, endorder, 0 }, + { nullptr, leaf, TWALK_END_MARKER }, + }; + twalk_action(tests4, leaf, TWALK_SET_DATA); + twalk(root, twalk_action); + twalk_action(nullptr, leaf, TWALK_CHECK_END); + + delete_node_recursive(root); +} diff --git a/Userland/Libraries/LibC/search.cpp b/Userland/Libraries/LibC/search.cpp index 9909e42cb6..783592c313 100644 --- a/Userland/Libraries/LibC/search.cpp +++ b/Userland/Libraries/LibC/search.cpp @@ -89,4 +89,28 @@ void* tfind(const void* key, void* const* rootp, int (*comparator)(const void*, return nullptr; } + +static void twalk_internal(const struct search_tree_node* node, void (*action)(const void*, VISIT, int), int depth) +{ + if (!node) + return; + + if (!node->right && !node->left) { + action(node, leaf, depth); + return; + } + + action(node, preorder, depth); + twalk_internal(node->left, action, depth + 1); + action(node, postorder, depth); + twalk_internal(node->right, action, depth + 1); + action(node, endorder, depth); +} + +void twalk(const void* rootp, void (*action)(const void*, VISIT, int)) +{ + auto node = static_cast(rootp); + + twalk_internal(node, action, 0); +} } diff --git a/Userland/Libraries/LibC/search.h b/Userland/Libraries/LibC/search.h index a9f4fd96e2..5d31272c42 100644 --- a/Userland/Libraries/LibC/search.h +++ b/Userland/Libraries/LibC/search.h @@ -8,7 +8,15 @@ __BEGIN_DECLS +typedef enum { + preorder, + postorder, + endorder, + leaf, +} VISIT; + void* tsearch(const void*, void**, int (*)(const void*, const void*)); void* tfind(const void*, void* const*, int (*)(const void*, const void*)); +void twalk(const void*, void (*)(const void*, VISIT, int)); __END_DECLS