Code

avltree: Added helper function sdb_avltree_valid().
authorSebastian Harl <sh@tokkee.org>
Wed, 9 Jul 2014 19:07:58 +0000 (21:07 +0200)
committerSebastian Harl <sh@tokkee.org>
Wed, 9 Jul 2014 19:07:58 +0000 (21:07 +0200)
The function walks the entire tree and checks each node for problems (e.g.
unbalanced nodes, wrong height, missing child/parent links).

Use the function in the unit tests (which is the primary use for the function
anyway ;-)).

src/include/utils/avltree.h
src/utils/avltree.c
t/unit/utils/avltree_test.c

index b3eec42f177e4752d1118cac5e321bf22c02e257..4e4d00c1b95f37742eab50cf7cd4226511ee8e60 100644 (file)
@@ -73,6 +73,9 @@ sdb_avltree_iter_get_next(sdb_avltree_iter_t *iter);
 size_t
 sdb_avltree_size(sdb_avltree_t *tree);
 
+_Bool
+sdb_avltree_valid(sdb_avltree_t *tree);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index d8151c8926cb9f443539225eda05514347d4384c..353a3849c2c307c975413d72a9dba64dfc0f7258 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "sysdb.h"
 #include "utils/avltree.h"
+#include "utils/error.h"
 
 #include <assert.h>
 
@@ -54,6 +55,7 @@ struct node {
        size_t height;
 };
 
+#define NODE_NAME(n) ((n) && (n)->obj ? (n)->obj->name : "<nil>")
 #define NODE_HEIGHT(n) ((n) ? (n)->height : (size_t)0)
 #define CALC_HEIGHT(n) (SDB_MAX(NODE_HEIGHT((n)->right), \
                        NODE_HEIGHT((n)->left)) + 1)
@@ -420,5 +422,56 @@ sdb_avltree_size(sdb_avltree_t *tree)
        return tree ? tree->size : 0;
 } /* sdb_avltree_size */
 
+_Bool
+sdb_avltree_valid(sdb_avltree_t *tree)
+{
+       node_t *n;
+
+       _Bool status = 1;
+       size_t size = 0;
+
+       if (! tree)
+               return 1;
+
+       for (n = node_smallest(tree); n; n = node_next(n)) {
+               int bf = BALANCE(n);
+
+               if ((bf < -1) || (1 < bf)) {
+                       sdb_log(SDB_LOG_ERR, "avltree: Unbalanced node '%s' (bf=%i)",
+                                       NODE_NAME(n), bf);
+                       status = 0;
+               }
+
+               if (NODE_HEIGHT(n) != n->height) {
+                       sdb_log(SDB_LOG_ERR, "avltree: Unexpected height for node '%s': "
+                                       "%zu; expected: %zu", NODE_NAME(n), n->height,
+                                       NODE_HEIGHT(n));
+                       status = 0;
+               }
+
+               if (n->parent && (n->parent->left != n) && (n->parent->right != n)) {
+                       sdb_log(SDB_LOG_ERR, "avltree: Invalid child nodes for parent of "
+                                       "node '%s': {left: %s, right: %s}; expected: %s",
+                                       NODE_NAME(n), NODE_NAME(n->parent->left),
+                                       NODE_NAME(n->parent->right), NODE_NAME(n));
+                       status = 0;
+               }
+               if ((! n->parent) && (n != tree->root)) {
+                       sdb_log(SDB_LOG_ERR, "avltree: Non-root node '%s' does not "
+                                       "have a parent", NODE_NAME(n));
+                       status = 0;
+               }
+
+               ++size;
+       }
+
+       if (size != tree->size) {
+               sdb_log(SDB_LOG_ERR, "avltree: Invalid size %zu; expected: %zu",
+                               tree->size, size);
+               status = 0;
+       }
+       return status;
+} /* sdb_avltree_valid */
+
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
 
index 9b48fdcb2ea44e8f60c53ee5c1474348e9d3d566..f1ea1fc09fd27baa7aac336cd3b3ba5047b43bbb 100644 (file)
@@ -130,6 +130,10 @@ START_TEST(test_insert)
                fail_unless(check == (int)i + 1,
                                "sdb_avltree_size(<tree>) = %d; expected: %zu",
                                check, i + 1);
+
+               fail_unless(sdb_avltree_valid(tree),
+                               "sdb_avltree_insert(<tree>, <%s>) left behind invalid tree",
+                               test_data[i].name);
        }
 
        /* and again ... now reporting errors because of duplicates */