diff --git a/docs/rbtree.txt b/docs/rbtree.txt index 02173a2..7955323 100644 --- a/docs/rbtree.txt +++ b/docs/rbtree.txt @@ -29,9 +29,6 @@ The binary tree node. `key`:: The key of this node -`data`:: - pointer to the data associated with the key - `child`:: pointers to the left and right child to this node @@ -51,16 +48,16 @@ Structure that holds a tree of nodes Pointer to the node that is the root of the tree. `delete_fn`:: - Pointer to the function that should handle the delete routines for the `rbnode->data` pointer. + Pointer to the function that should handle the delete routines for the `rbnode->key` pointer. `update_fn`:: - Pointer to the function that is called when the implementation performs an update of an `rbnode->data` pointer. + + Pointer to the function that is called when the implementation performs an update of an `rbnode->key` pointer. + The function gets the following information passed in order: old pointer, new pointer. NOTE: You may only need this if you store the data in another structure and has to keep it synchronized with the RB-tree. `cmp_fn`:: - Pointer to the function that is used to compare `rbnode->data` pointers. + + Pointer to the function that is used to compare two `rbnode->key` pointers. + Shall return a value greater than zero if 'ptr1' > 'ptr2', a value less than zero if 'ptr1' < 'ptr2' and zero if 'ptr1' == 'ptr2'. -- @@ -73,7 +70,7 @@ Functions If provided, calls `rbtree->update_fn` and `rbtree->delete_fn` if a node should be updated. + Returns a nonzero value if a new node was inserted, zero if 'key' already existed and that node was updated. -NOTE: The memory pointed to by the 'data' pointer is *not* copied so you must ensure that it has a infinite lifetime. +NOTE: The memory pointed to by the 'key' pointer is *not* copied so you must ensure that it has a infinite lifetime. `rbtree_delete()`:: @@ -92,11 +89,8 @@ NOTE: The memory pointed to by the 'data' pointer is *not* copied so you must en Searches the tree for 'key'. + Returns a pointer to the node if found else `NULL`. - -`rbtree_cmp_search()`:: - Searches the tree by compareing 'cmpdata' and `rbnode->data` to find a match. + - Returns the node if found, `NULL` otherwise. +NOTE: the function uses the `rbtree->cmp_fn` function to compare 'key' with a `rbnode->key`. `rbtree_is_empty()`:: diff --git a/src/rbtree.c b/src/rbtree.c index 6e9f63b..08439cb 100644 --- a/src/rbtree.c +++ b/src/rbtree.c @@ -18,12 +18,11 @@ #define is_red(n) ((n) != NULL && (n)->color == RB_RED) #define swap(n,d,q) ((n)->child[(n)->child[d] == (q)]) -static rbnode* node_alloc(unsigned key, void *data) { +static rbnode* node_alloc(const void *key) { rbnode *n = xmalloc(sizeof(rbnode)); n->key = key; - n->data = data; n->color = RB_RED; n->child[0] = NULL; n->child[1] = NULL; @@ -40,11 +39,11 @@ static void node_dealloc(rbnode *n, void (*dealloc)(void *)) { return; if (dealloc) - dealloc(n->data); - + dealloc((void *)n->key); + node_dealloc(n->child[0], dealloc); node_dealloc(n->child[1], dealloc); - + free(n); } @@ -61,22 +60,6 @@ static void rbwalk(rbnode *n, void (*action)(rbnode *)) { rbwalk(n->child[1], action); } -static rbnode* rbcmp(rbnode *n, int (*cmp_fn)(const char *, const char *), const void *cmpdata) { - - if (!n) - return NULL; - - if (cmp_fn(n->data, cmpdata) == 0) - return n; - - rbnode *r = rbcmp(n->child[0], cmp_fn, cmpdata); - - if (!r) - r = rbcmp(n->child[1], cmp_fn, cmpdata); - - return r; -} - static inline rbnode* rotate_single(rbnode *root, unsigned char dir) { rbnode *save = root->child[!dir]; @@ -103,34 +86,27 @@ inline int rbtree_is_empty(rbtree *tree) { /* * Searches a tree by key. */ -rbnode* rbtree_search(rbtree *tree, unsigned key) { +rbnode* rbtree_search(rbtree *tree, const void *key) { rbnode *n; - if (tree == NULL || tree->root == NULL) + if (tree == NULL || tree->root == NULL || tree->cmp_fn == NULL) return NULL; n = tree->root; while(n) { - - if (n->key == key) + int cmp = tree->cmp_fn(n->key, key); + + if (cmp == 0) break; - - n = n->child[n->key < key]; + + n = n->child[cmp < 0]; } return n; } -rbnode* rbtree_cmp_search(rbtree *tree, const void *cmpdata) { - - if (tree == NULL || tree->cmp_fn == NULL) - return NULL; - - return rbcmp(tree->root, tree->cmp_fn, cmpdata); -} - void rbtree_walk(rbtree *tree, void (*action)(rbnode *)) { if (tree == NULL || action == NULL) @@ -148,7 +124,7 @@ void rbtree_free(rbtree *tree) { tree->root = NULL; } -int rbtree_insert(rbtree *tree, unsigned key, void *data) { +int rbtree_insert(rbtree *tree, const void *key) { rbnode head = {0}; @@ -159,9 +135,12 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) { rbnode *p, *q; unsigned char dir = 0, dir2, last, inserted = 0; - + + if (!tree || !tree->cmp_fn) + return 0; + if (tree->root == NULL) { - tree->root = node_alloc(key, data); + tree->root = node_alloc(key); inserted = 1; goto done; } @@ -173,8 +152,10 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) { /* somewhere in here, there should be dragons */ for(;;) { + int cmp; + if (q == NULL) { - p->child[dir] = q = node_alloc(key, data); + p->child[dir] = q = node_alloc(key); inserted = 1; } else if (is_red(q->child[0]) && is_red(q->child[1])) { /* color flip case */ @@ -191,12 +172,14 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) { else t->child[dir2] = rotate_double(g, !last); } + + cmp = tree->cmp_fn(q->key, key); - if (q->key == key) + if (cmp == 0) break; last = dir; - dir = q->key < key; + dir = cmp < 0; if (g) t = g; @@ -205,11 +188,9 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) { } if (!inserted) { - if (tree->update_fn) - tree->update_fn(q->data, data); if (tree->delete_fn) - tree->delete_fn(q->data); - q->data = data; + tree->delete_fn((void*)q->key); + q->key = key; } tree->root = head.child[1]; @@ -221,7 +202,7 @@ done: return inserted; } -int rbtree_delete(rbtree *tree, unsigned key) { +int rbtree_delete(rbtree *tree, const void *key) { rbnode head = {0}; @@ -233,7 +214,7 @@ int rbtree_delete(rbtree *tree, unsigned key) { unsigned char dir = 1, dir2, last; - if (rbtree_is_empty(tree)) + if (rbtree_is_empty(tree) || !tree->cmp_fn) return 0; q = &head; @@ -243,13 +224,15 @@ int rbtree_delete(rbtree *tree, unsigned key) { /* more dragons (killed some of them though) */ while(q->child[dir]) { - last = dir; + int cmp; g = p, p = q; - q = q->child[dir]; - dir = q->key < key; - - if (q->key == key) + q = q->child[dir]; + last = dir; + + cmp = tree->cmp_fn(q->key, key); + dir = cmp < 0; + if (cmp == 0) f = q; if (is_red(q) || is_red(q->child[dir])) @@ -288,12 +271,10 @@ int rbtree_delete(rbtree *tree, unsigned key) { /* remove if found */ if (f) { if (tree->delete_fn) - tree->delete_fn(f->data); + tree->delete_fn((void*)f->key); - if (f != q) { - f->key = q->key; - f->data = q->data; - } + if (f != q) + f->key = q->key; swap(p, 1, q) = swap(q, 0, NULL); xfree(q); return 1; diff --git a/src/rbtree.h b/src/rbtree.h index 14fddd0..96688b1 100644 --- a/src/rbtree.h +++ b/src/rbtree.h @@ -18,8 +18,7 @@ /* node definition */ typedef struct _rbn { - unsigned key; - void *data; + const void *key; struct _rbn *child[2]; unsigned char color; } rbnode; @@ -29,24 +28,21 @@ typedef struct { /* user defined operations */ void (*delete_fn)(void *); void (*update_fn)(void *, void *); - /* note: char* is used here to make dereference easier inside the function */ - int (*cmp_fn)(const char *, const char *); + int (*cmp_fn)(const void *, const void *); } rbtree; #define RBTREE_INIT(delete, update, cmp) { NULL, delete, update, cmp} int rbtree_is_empty(rbtree *tree); -rbnode* rbtree_search(rbtree *tree, unsigned key); - -rbnode* rbtree_cmp_search(rbtree *tree, const void *data); +rbnode* rbtree_search(rbtree *tree, const void *key); void rbtree_walk(rbtree *tree, void (*action)(rbnode *)); void rbtree_free(rbtree *tree); -int rbtree_insert(rbtree *tree, unsigned key, void *data); +int rbtree_insert(rbtree *tree, const void *key); -int rbtree_delete(rbtree *tree, unsigned key); +int rbtree_delete(rbtree *tree, const void *key); #endif /* __RBTREE_H */ diff --git a/test/t_rbtree.c b/test/t_rbtree.c index 90eb3ec..d79c837 100644 --- a/test/t_rbtree.c +++ b/test/t_rbtree.c @@ -16,6 +16,9 @@ #define is_red(n) ((n) != NULL && (n)->color == RB_RED) +static int vcmp(const void *a, const void *b); +static void vdelete(void *ptr); + static int rb_assert(rbnode *node) { int rh, lh; @@ -38,9 +41,9 @@ static int rb_assert(rbnode *node) { lh = rb_assert(ln); rh = rb_assert(rn); - - if ( (ln != NULL && ln->key >= node->key) && - (rn != NULL && rn->key <= node->key) ) { + + if ( (ln != NULL && vcmp(ln->key, node->key) >= 0) && + (rn != NULL && vcmp(rn->key, node->key) <= 0) ) { die("BST violation"); return 0; } @@ -58,61 +61,33 @@ static int rb_assert(rbnode *node) { return 0; } -static int vcmp(const char *a, const char *b); -static void vfree(void *ptr); -static void vupdate(void *old, void *new); - /* data */ -static rbtree tree = RBTREE_INIT(vfree, vupdate, vcmp); +static rbtree tree = RBTREE_INIT(vdelete, NULL, vcmp); static int keyref[NODES]; -static char *dataref[NODES]; -static int vcmp(const char *a, const char *b) { +static int vcmp(const void *a, const void *b) { - printf("cmp: %s -> %s\n", a, b); - - return strcmp(a, b); + return *((int*)a) - *((int*)b); } -static void vupdate(void *old, void *new) { +static void vdelete(void *ptr) { int i; - printf("update: %s -> %s\n", (char*)old, (char*)new); - + printf("delete: %i\n", *((int*)ptr)); + for(i=0; i < NODES; i++) { if (keyref[i] == -1) continue; - - if (old == dataref[i]) { - dataref[i] = new; + + if (ptr == &keyref[i]) { + keyref[i] = -1; break; } } } -static void vfree(void *ptr) { - - int i; - - printf("free: %s\n", (char*)ptr); - - for(i=0; i < NODES; i++) { - - if (keyref[i] == -1) - continue; - - if (ptr == dataref[i]) { - keyref[i] = -1; - dataref[i] = NULL; - break; - } - } - - free(ptr); -} - static void vwalk(rbnode *node) { int i, found = 0; @@ -120,46 +95,36 @@ static void vwalk(rbnode *node) { /* check if this node exist in the reference list */ for(i=0; i < NODES; i++) { - if (node->key == keyref[i] && node->data == dataref[i]) { + if (node->key == &keyref[i]) { found = 1; break; } } - assert(found); } void test_insert() { - int i = 0, ret; - uint ckey; - char *data; - - utest_init_RNG(); - + int i = 0; + /* insert values */ while(i < NODES) { - - ckey = (uint) (rand() % MAX_VAL); - data = utest_ran_string(16); + + rbnode *node; + + keyref[i] = (uint) (rand() % MAX_VAL); /* insert into rbtree and assert it */ - ret = rbtree_insert(&tree, ckey, data); + node = rbtree_insert(&tree, &keyref[i]); rb_assert(tree.root); - if (!ret) - continue; - - printf("insert: %i %s\n", ckey, data); - - keyref[i] = ckey; - dataref[i] = data; + assert(node); + + printf("insert: %i\n", *((int*)node->key)); i++; } - /* insert duplicate key */ - rbtree_insert(&tree, keyref[rand() % NODES], strdup("---- update ----")); rb_assert(tree.root); } @@ -174,8 +139,10 @@ void test_delete() { key = keyref[rand() % NODES]; while(key < 0); - rbtree_delete(&tree, key); + rbtree_delete(&tree, &key); rb_assert(tree.root); + + keyref[key] = -1; } } @@ -192,40 +159,23 @@ void test_search() { printf("search: expecting to find key %i\n", keyref[index]); /* search for a key we know exists */ - n = rbtree_search(&tree, keyref[index]); + n = rbtree_search(&tree, &keyref[index]); rb_assert(tree.root); assert(n != NULL); - assert(n->key == keyref[index]); - assert_string(n->data, dataref[index]); + assert(n->key == &keyref[index]); - printf("search: expecting to not find key: %i\n", MAX_VAL+512); + index = MAX_VAL + 512; + + printf("search: expecting to not find key: %i\n", index); /* search for a key we now don't exist */ - n = rbtree_search(&tree, MAX_VAL+512); + n = rbtree_search(&tree, &index); rb_assert(tree.root); assert(n == NULL); } -void test_cmp_search() { - - char *search; - rbnode *n; - - do - search = dataref[rand()%NODES]; - while(search == NULL); - - printf("cmp_search: searching for %s\n", search); - - n = rbtree_cmp_search(&tree, search); - - rb_assert(tree.root); - assert(n != NULL); - assert_string(n->data, search); -} - void test_walk() { rbtree_walk(&tree, vwalk); @@ -235,6 +185,8 @@ void test_walk() { int main(int argc, char **argv) { tree.root = NULL; + + utest_init_RNG(); /* a new tree is empty */ assert(rbtree_is_empty(&tree)); @@ -242,13 +194,11 @@ int main(int argc, char **argv) { test_insert(); test_search(); - test_cmp_search(); test_walk(); test_delete(); test_search(); - test_cmp_search(); test_walk(); /* free the tree */