From a6ed89d8cd9a99174a7d4cc28264c30f55aa2538 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 2 Feb 2023 14:10:35 +0100 Subject: [PATCH] feat(tree): add tree iterator --- include/vieter_tree.h | 24 +++++++++++++++- src/tree/vieter_tree.c | 8 +----- src/tree/vieter_tree_internal.h | 12 ++++++++ src/tree/vieter_tree_iterator.c | 49 +++++++++++++++++++++++++++++++++ src/tree/vieter_tree_node.c | 19 +++++++++++++ src/tree/vieter_tree_node.h | 6 ++++ test/tree/test_tree.c | 15 ++++++++-- 7 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 src/tree/vieter_tree_iterator.c diff --git a/include/vieter_tree.h b/include/vieter_tree.h index d3abd2f..2079eb9 100644 --- a/include/vieter_tree.h +++ b/include/vieter_tree.h @@ -9,7 +9,8 @@ typedef struct vieter_tree vieter_tree; typedef enum vieter_tree_error { vieter_tree_ok = 0, vieter_tree_already_present = 1, - vieter_tree_not_present = 2 + vieter_tree_not_present = 2, + vieter_tree_iterator_done = 3 } vieter_tree_error; /* @@ -45,4 +46,25 @@ vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree, */ uint64_t vieter_tree_size(vieter_tree *tree); +/* + * An iterator that can be used to traverse a tree in-order. + */ +typedef struct vieter_tree_iterator vieter_tree_iterator; + +/* + * Allocate and initialize a new iterator from the given tree. + */ +vieter_tree_iterator *vieter_tree_iterator_from(vieter_tree *tree); + +/* + * Free an iterator. + */ +void vieter_tree_iterator_free(vieter_tree_iterator **ptp); + +/* + * Return the iterator's next element. + */ +vieter_tree_error vieter_tree_iterator_next(void **out, + vieter_tree_iterator *iter); + #endif diff --git a/src/tree/vieter_tree.c b/src/tree/vieter_tree.c index eef2aef..84a1a26 100644 --- a/src/tree/vieter_tree.c +++ b/src/tree/vieter_tree.c @@ -1,11 +1,5 @@ -#include "vieter_tree_internal.h" -#include "vieter_tree_node.h" #include "vieter_tree_balancing.h" - -struct vieter_tree { - uint64_t size; - vieter_tree_node *root; -}; +#include "vieter_tree_internal.h" vieter_tree *vieter_tree_init() { return calloc(1, sizeof(vieter_tree)); } diff --git a/src/tree/vieter_tree_internal.h b/src/tree/vieter_tree_internal.h index 670d45b..855139f 100644 --- a/src/tree/vieter_tree_internal.h +++ b/src/tree/vieter_tree_internal.h @@ -1,10 +1,22 @@ #include "vieter_tree.h" +#include "vieter_tree_node.h" #include +struct vieter_tree { + uint64_t size; + vieter_tree_node *root; +}; + /* * Inspect whether the tree is still a valid red-black-tree. * * @return true if valid, false otherwise. */ bool vieter_tree_validate(vieter_tree *tree); + +struct vieter_tree_iterator { + vieter_tree_node *current_node; + bool started; + bool done; +}; diff --git a/src/tree/vieter_tree_iterator.c b/src/tree/vieter_tree_iterator.c new file mode 100644 index 0000000..4eefe02 --- /dev/null +++ b/src/tree/vieter_tree_iterator.c @@ -0,0 +1,49 @@ +#include "vieter_tree.h" +#include "vieter_tree_internal.h" + +vieter_tree_iterator *vieter_tree_iterator_from(vieter_tree *tree) { + vieter_tree_iterator *iter = calloc(1, sizeof(vieter_tree_iterator)); + + // An empty tree's iterator will be done immediately + if (tree->size == 0) { + iter->started = true; + iter->done = true; + + return iter; + } + + iter->current_node = tree->root; + + return iter; +} + +vieter_tree_error vieter_tree_iterator_next(void **out, + vieter_tree_iterator *iter) { + if (iter->done) { + return vieter_tree_iterator_done; + } + + if (!iter->started) { + while (iter->current_node->children[0] != NULL) { + iter->current_node = iter->current_node->children[0]; + } + + iter->started = true; + } + + *out = iter->current_node->data; + + iter->current_node = vieter_tree_node_next(iter->current_node); + + if (iter->current_node == NULL) { + iter->done = true; + } + + return vieter_tree_ok; +} + +void vieter_tree_iterator_free(vieter_tree_iterator **ptp) { + free(*ptp); + + *ptp = NULL; +} diff --git a/src/tree/vieter_tree_node.c b/src/tree/vieter_tree_node.c index e58092e..0a8e658 100644 --- a/src/tree/vieter_tree_node.c +++ b/src/tree/vieter_tree_node.c @@ -150,3 +150,22 @@ void vieter_tree_node_set(vieter_tree_node *node, vieter_tree_node_flag flag, bool vieter_tree_node_get(vieter_tree_node *node, vieter_tree_node_flag flag) { return (node->flags & flag) != 0; } + +vieter_tree_node *vieter_tree_node_next(vieter_tree_node *node) { + if (node->children[1] != NULL) { + node = node->children[1]; + + while (node->children[0] != NULL) { + node = node->children[0]; + } + + return node; + } + + while (node->parent != NULL && + vieter_tree_node_get(node, vieter_tree_node_right)) { + node = node->parent; + } + + return node->parent; +} diff --git a/src/tree/vieter_tree_node.h b/src/tree/vieter_tree_node.h index 3b1c169..3cf8e56 100644 --- a/src/tree/vieter_tree_node.h +++ b/src/tree/vieter_tree_node.h @@ -77,4 +77,10 @@ void vieter_tree_node_set_children(vieter_tree_node *parent, vieter_tree_node ** */ void vieter_tree_node_set_child(vieter_tree_node *parent, vieter_tree_node *child, bool right); +/* + * Return the in-order successor of the given node, or NULL if it's the last + * node in the tree. + */ +vieter_tree_node *vieter_tree_node_next(vieter_tree_node *node); + #endif diff --git a/test/tree/test_tree.c b/test/tree/test_tree.c index 2ab6870..10235cb 100644 --- a/test/tree/test_tree.c +++ b/test/tree/test_tree.c @@ -16,19 +16,30 @@ void test_insert() { vieter_tree *tree = vieter_tree_init(); for (uint64_t i = 0; i < 250; i++) { - TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + TEST_CHECK(vieter_tree_insert(tree, i, (void *)i) == vieter_tree_ok); TEST_SIZE(tree, i + 1); TEST_CHECK(vieter_tree_validate(tree)); } - void *out; + vieter_tree_iterator *iter = vieter_tree_iterator_from(tree); + + void *out = NULL; for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(out == (void *)i); TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); + + out = NULL; + + TEST_CHECK(vieter_tree_iterator_next(&out, iter) == vieter_tree_ok); + TEST_CHECK(out == (void *)i); } + TEST_CHECK(vieter_tree_iterator_next(&out, iter) == vieter_tree_iterator_done); + + vieter_tree_iterator_free(&iter); vieter_tree_free(tree); }