diff --git a/src/rbtree.c b/src/rbtree.c index d7e7a0d..74974a6 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -12,6 +12,10 @@ */ #include #include +#ifdef __DEBUG__ +#include +#include +#endif #include "xalloc.h" #include "rbtree.h" @@ -47,6 +51,60 @@ static void node_dealloc(rbnode *n, void (*dealloc)(void *)) { free(n); } +#ifdef __DEBUG__ +#define rb_err(msg) fputs("rbtree error: " msg, stderr) + +static int rb_assert_r(rbnode *node, int (*cmp)(const void *, const void *)) { + + int rh, lh; + rbnode *ln, *rn; + + if (node == NULL) + return 1; + + ln = node->child[0]; + rn = node->child[1]; + + if (is_red(node)) { + /* double red violation */ + if (is_red(ln) || is_red(rn)) { + rb_err("Double red violation"); + return 0; + } + } + + lh = rb_assert_r(ln, cmp); + rh = rb_assert_r(rn, cmp); + + if ( (ln && cmp(ln->key, node->key) >= 0) && + (rn && cmp(rn->key, node->key) <= 0) ) { + rb_err("Binary tree violation"); + return 0; + } + + if (rh != 0 && lh != 0) { + + if (rh != lh) { + rb_err("Black height violation"); + return 0; + } + + return is_red(node) ? lh : lh+1; + } + + return 0; +} +#undef rb_err + +static void rb_assert(rbtree *tree) { + + if (!tree || !tree->cmp_fn) + return; + + assert(rb_assert_r(tree->root, tree->cmp_fn)); +} +#endif /* __DEBUG__ */ + /* * Recursivly walks a tree, applying action function on every node */ @@ -112,6 +170,10 @@ void rbtree_walk(rbtree *tree, void (*action)(const void *)) { return; rbwalk(tree->root, action); + +#ifdef __DEBUG__ + rb_assert(tree); +#endif } void rbtree_free(rbtree *tree) { @@ -121,6 +183,10 @@ void rbtree_free(rbtree *tree) { node_dealloc(tree->root, tree->delete_fn); tree->root = NULL; + +#ifdef __DEBUG__ + rb_assert(tree); +#endif } int rbtree_insert(rbtree *tree, const void *key) { @@ -196,6 +262,10 @@ done: /* root should be black */ tree->root->color = RB_BLACK; +#ifdef __DEBUG__ + rb_assert(tree); +#endif + return 1; } @@ -276,5 +346,9 @@ int rbtree_delete(rbtree *tree, const void *key) { if (tree->root) tree->root->color = RB_BLACK; +#ifdef __DEBUG__ + rb_assert(tree); +#endif + return f != NULL; } diff --git a/test/t_rbtree.c b/test/t_rbtree.c index 0b2c4cf..8ec9583 100644 --- a/test/t_rbtree.c +++ b/test/t_rbtree.c @@ -1,10 +1,4 @@ -/* - * rb_assert should be called after every rbtree_* operation (not just only the one's - * that actualy performs changes to the tree) to ensure the tree is valid after the - * function is done. - */ - #include #include #include @@ -14,8 +8,6 @@ #define NODES 3000 #define MAX_VAL (3*(NODES/4)) -#define is_red(n) ((n) != NULL && (n)->color == RB_RED) - static int vcmp(const void *a, const void *b); static void vdelete(void *ptr); @@ -23,48 +15,6 @@ static void vdelete(void *ptr); static rbtree tree = RBTREE_INIT(vdelete, NULL, vcmp); static int keyref[NODES]; -static int rb_assert(rbnode *node) { - - int rh, lh; - rbnode *ln, *rn; - - if (node == NULL) { - return 1; - } - - ln = node->child[0]; - rn = node->child[1]; - - if (is_red(node)) { - /* double red violation */ - if (is_red(ln) || is_red(rn)) { - die("Double red"); - return 0; - } - } - - lh = rb_assert(ln); - rh = rb_assert(rn); - - if ( (ln != NULL && vcmp(ln->key, node->key) >= 0) && - (rn != NULL && vcmp(rn->key, node->key) <= 0) ) { - die("BST violation"); - return 0; - } - - if (rh != 0 && lh != 0) { - - if (rh != lh) { - die("Black height violation"); - return 0; - } - - return (is_red(node)) ? lh : lh+1; - } - - return 0; -} - static int vcmp(const void *a, const void *b) { return *((int*)a) - *((int*)b); @@ -119,10 +69,8 @@ static void setup(int sorted) { if (sorted) qsort(keyref, NODES, sizeof(keyref[0]), vcmp); - for(i=0; i < NODES; i++) { + for(i=0; i < NODES; i++) rbtree_insert(&tree, &keyref[i]); - rb_assert(tree.root); - } } static void teardown() { @@ -130,7 +78,6 @@ static void teardown() { int i; rbtree_free(&tree); - rb_assert(tree.root); for(i=0; i < NODES; i++) keyref[i] = -1; @@ -141,13 +88,10 @@ void test_rbtree_is_empty() { setup(0); assert(rbtree_is_empty(&tree) == 0); - rb_assert(tree.root); rbtree_free(&tree); - rb_assert(tree.root); assert(rbtree_is_empty(&tree)); - rb_assert(tree.root); teardown(); } @@ -164,13 +108,11 @@ void test_rbtree_delete() { continue; assert(rbtree_delete(&tree, &keyref[i])); - rb_assert(tree.root); } /* delete a key that does not exist */ i = MAX_VAL + 512; assert(rbtree_delete(&tree, &i) == 0); - rb_assert(tree.root); teardown(); } @@ -187,11 +129,9 @@ void test_rbtree_delete_all() { continue; assert(rbtree_delete(&tree, &keyref[i])); - rb_assert(tree.root); } assert(rbtree_is_empty(&tree)); - rb_assert(tree.root); teardown(); } @@ -201,7 +141,6 @@ void test_rbtree_walk() { setup(1); rbtree_walk(&tree, walk_fn); - rb_assert(tree.root); teardown(); } @@ -214,12 +153,10 @@ void test_rbtree_search() { s = keyref[NODES/2]; f = rbtree_search(&tree, &s); - rb_assert(tree.root); assert(f && *f == s); s = MAX_VAL + 512; f = rbtree_search(&tree, &s); - rb_assert(tree.root); assert(f == NULL); teardown();