return x->data;
}
+/** Initialise a pre-order iterator
+ *
+ * @note If iteration ends early because of a loop condition #rbtree_iter_done must be called.
+ *
+ * @param[out] iter to initialise.
+ * @param[in] tree to iterate over.
+ * @return
+ * - The first node. Mutex will be held.
+ * - NULL if the tree is empty.
+ */
+void *rbtree_iter_init_preorder(fr_rb_tree_iter_t *iter, rbtree_t *tree)
+{
+ fr_rb_node_t *x = tree->root;
+
+ if (x == NIL) return NULL;
+
+ if (tree->lock) pthread_mutex_lock(&tree->mutex);
+
+ /*
+ * First, the root.
+ */
+ *iter = (fr_rb_tree_iter_t){
+ .tree = tree,
+ .node = x
+ };
+
+ return x->data;
+}
+
+/** Return the next node
+ *
+ * @note Will unlock the tree if no more elements remain.
+ *
+ * @param[in] iter previously initialised with #rbtree_iter_init
+ * @return
+ * - The next node.
+ * - NULL if no more nodes remain.
+ */
+void *rbtree_iter_next_preorder(fr_rb_tree_iter_t *iter)
+{
+ fr_rb_node_t *x = iter->node, *y;
+
+ /*
+ * Catch callers repeatedly calling iterator
+ * at the end.
+ */
+ if (unlikely(iter->node == NIL)) return NULL;
+
+ /*
+ * Next is a child of the just-returned node, if it has one.
+ * (Left child first.)
+ */
+ if (x->left != NIL) {
+ x = x->left;
+ iter->node = x;
+ return x->data;
+ }
+ if (x->right != NIL) {
+ x = x->right;
+ iter->node = x;
+ return x->data;
+ }
+
+ /*
+ * Otherwise, the nearest ancestor's unreturned right
+ * child, if one exists.
+ */
+ for (; (y = x->parent) != NIL; x = y) {
+ if (y->right != NIL && y->right != x) {
+ x = y->right;
+ iter->node = x;
+ return x->data;
+ }
+ }
+
+ /*
+ * None of the above? We're done.
+ */
+ iter->node = NIL;
+ if (iter->tree->lock)
+ pthread_mutex_unlock(&iter->tree->mutex);
+ return NULL;
+}
+
+/** Initialise a pre-order iterator
+ *
+ * @note If iteration ends early because of a loop condition #rbtree_iter_done must be called.
+ *
+ * @param[out] iter to initialise.
+ * @param[in] tree to iterate over.
+ * @return
+ * - The first node. Mutex will be held.
+ * - NULL if the tree is empty.
+ */
+void *rbtree_iter_init_postorder(fr_rb_tree_iter_t *iter, rbtree_t *tree)
+{
+ fr_rb_node_t *x = tree->root;
+
+ if (x == NIL) return NULL;
+
+ if (tree->lock) pthread_mutex_lock(&tree->mutex);
+
+ /*
+ * First: the deepest leaf to the left (jogging to the
+ * right if there's a right child but no left).
+ */
+ for (;;) {
+ for (; x->left != NIL; x = x->left) ;
+ if (x->right == NIL) break;
+ x = x->right;
+ }
+
+ *iter = (fr_rb_tree_iter_t){
+ .tree = tree,
+ .node = x
+ };
+
+ return x->data;
+}
+
+/** Return the next node
+ *
+ * @note Will unlock the tree if no more elements remain.
+ *
+ * @param[in] iter previously initialised with #rbtree_iter_init
+ * @return
+ * - The next node.
+ * - NULL if no more nodes remain.
+ */
+void *rbtree_iter_next_postorder(fr_rb_tree_iter_t *iter)
+{
+ fr_rb_node_t *x = iter->node, *y;
+
+ /*
+ * Catch callers repeatedly calling iterator
+ * at the end.
+ */
+ if (unlikely(iter->node == NIL)) return NULL;
+
+ /*
+ * This is postorder, so a just-returned node's
+ * descendants have all been returned. If there
+ * is another node, it's an ancestor or one of
+ * its not-yet-returned descendants...but if
+ * we're at the root, we're done.
+ */
+ y = x->parent;
+ if (y == NIL) {
+ iter->node = NIL;
+ if (iter->tree->lock) pthread_mutex_unlock(&iter->tree->mutex);
+ return NULL;
+ }
+
+ /*
+ * Return the parent if it has no right child, or it has one but
+ * it's been returned.
+ */
+ if (y->right == NIL || y->right == x) {
+ iter->node = y;
+ return y->data;
+ }
+
+ /*
+ * Otherwise, it's as if we're starting over with the right child.
+ */
+ x = y->right;
+ for (;;) {
+ for (; x->left != NIL; x = x->left) ;
+ if (x->right == NIL) break;
+ x = x->right;
+ }
+
+ iter->node = x;
+ return x->data;
+}
+
/** Explicitly unlock the tree
*
* @note Must be called if iterating over the tree ends early.
void *rbtree_iter_next_inorder(fr_rb_tree_iter_t *iter) CC_HINT(nonnull);
+void *rbtree_iter_init_preorder(fr_rb_tree_iter_t *iter, rbtree_t *tree) CC_HINT(nonnull);
+
+void *rbtree_iter_next_preorder(fr_rb_tree_iter_t *iter) CC_HINT(nonnull);
+
+void *rbtree_iter_init_postorder(fr_rb_tree_iter_t *iter, rbtree_t *tree) CC_HINT(nonnull);
+
+void *rbtree_iter_next_postorder(fr_rb_tree_iter_t *iter) CC_HINT(nonnull);
+
void rbtree_iter_done(fr_rb_tree_iter_t *iter) CC_HINT(nonnull);
#ifdef __cplusplus
return CMP(a->num, b->num);
}
-static void test_rbtree_iter(void)
+static void test_rbtree_iter_inorder(void)
{
rbtree_t *t;
fr_rb_test_node_t sorted[MAXSIZE];
size_t n, i;
fr_rb_tree_iter_t iter;
+ TEST_CASE("in-order iterator");
t = rbtree_alloc(NULL, fr_rb_test_node_t, node, fr_rb_test_cmp, NULL, RBTREE_FLAG_LOCK);
TEST_CHECK(t != NULL);
talloc_free(t);
}
+/*
+ * There's no natural test for pre- and post-order traversal
+ * as there is for in-order, so we must content ourselves
+ * with static test data.
+ */
+static uint32_t pre_post_input[] = {0, 15, 256, 49, 3, 8192, 144, 4, 4096, 25194};
+static uint32_t pre_output[] = {15, 3, 0, 4, 256, 49, 144, 8192, 4096, 25194};
+static uint32_t post_output[] = {0, 4, 3, 144, 49, 4096, 25194, 8192, 256, 15};
+
+static void test_rbtree_iter_preorder(void)
+{
+ rbtree_t *t;
+ fr_rb_test_node_t *p;
+ size_t i;
+ fr_rb_tree_iter_t iter;
+
+ TEST_CASE("pre-order iterator");
+ /*
+ * Build a tree from pre_post_input.
+ */
+ t = rbtree_alloc(NULL, fr_rb_test_node_t, node, fr_rb_test_cmp, NULL, RBTREE_FLAG_LOCK);
+ TEST_CHECK(t != NULL);
+
+ for (i = 0; i < sizeof(pre_post_input) / sizeof(uint32_t); i++) {
+ p = talloc(t, fr_rb_test_node_t);
+ p->num = pre_post_input[i];
+ rbtree_insert(t, p);
+ }
+
+ for (p = rbtree_iter_init_preorder(&iter, t), i = 0;
+ p;
+ p = rbtree_iter_next_preorder(&iter), i++) {
+ TEST_MSG("Checking pre_output[%zu] = %u vs n = %u", i, pre_output[i], p->num);
+ TEST_CHECK(pre_output[i] == p->num);
+ TEST_CHECK(pthread_mutex_trylock(&t->mutex) != 0); /* Lock still held */
+ }
+
+ TEST_CHECK(pthread_mutex_trylock(&t->mutex) == 0); /* Lock released */
+ pthread_mutex_unlock(&t->mutex);
+
+ talloc_free(t);
+}
+
+static void test_rbtree_iter_postorder(void)
+{
+ rbtree_t *t;
+ fr_rb_test_node_t *p;
+ size_t i;
+ fr_rb_tree_iter_t iter;
+
+ TEST_CASE("post-order iterator");
+ /*
+ * Build a tree from pre_post_input.
+ */
+ t = rbtree_alloc(NULL, fr_rb_test_node_t, node, fr_rb_test_cmp, NULL, RBTREE_FLAG_LOCK);
+ TEST_CHECK(t != NULL);
+
+ for (i = 0; i < sizeof(pre_post_input) / sizeof(uint32_t); i++) {
+ p = talloc(t, fr_rb_test_node_t);
+ p->num = pre_post_input[i];
+ rbtree_insert(t, p);
+ }
+
+ for (p = rbtree_iter_init_postorder(&iter, t), i = 0;
+ p;
+ p = rbtree_iter_next_postorder(&iter), i++) {
+ TEST_MSG("Checking post_output[%zu] s = %u vs n = %u", i, post_output[i], p->num);
+ TEST_CHECK(post_output[i] == p->num);
+ TEST_CHECK(pthread_mutex_trylock(&t->mutex) != 0); /* Lock still held */
+ }
+
+ TEST_CHECK(pthread_mutex_trylock(&t->mutex) == 0); /* Lock released */
+ pthread_mutex_unlock(&t->mutex);
+
+ talloc_free(t);
+}
+
TEST_LIST = {
- { "rbtree_iter", test_rbtree_iter },
+ { "rbtree_iter_inorder", test_rbtree_iter_inorder },
+ { "rbtree_iter_preorder", test_rbtree_iter_preorder },
+ { "rbtree_iter_postorder", test_rbtree_iter_postorder },
{ NULL }
};