Archived
1
0
Fork 0

rbtree.c: use generic keys.

to make the rbtree more usefull. allow generic keys and user defined compare functions.
This commit is contained in:
Henrik Hautakoski 2010-12-27 10:17:01 +01:00
parent f76f494dcc
commit 027bf154a7
4 changed files with 85 additions and 164 deletions

View file

@ -29,9 +29,6 @@ The binary tree node.
`key`:: `key`::
The key of this node The key of this node
`data`::
pointer to the data associated with the key
`child`:: `child`::
pointers to the left and right child to this node 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. Pointer to the node that is the root of the tree.
`delete_fn`:: `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`:: `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. 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. 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`:: `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'. 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. + 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. 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()`:: `rbtree_delete()`::
@ -93,10 +90,7 @@ NOTE: The memory pointed to by the 'data' pointer is *not* copied so you must en
Searches the tree for 'key'. + Searches the tree for 'key'. +
Returns a pointer to the node if found else `NULL`. Returns a pointer to the node if found else `NULL`.
`rbtree_cmp_search()`:: NOTE: the function uses the `rbtree->cmp_fn` function to compare 'key' with a `rbnode->key`.
Searches the tree by compareing 'cmpdata' and `rbnode->data` to find a match. +
Returns the node if found, `NULL` otherwise.
`rbtree_is_empty()`:: `rbtree_is_empty()`::

View file

@ -18,12 +18,11 @@
#define is_red(n) ((n) != NULL && (n)->color == RB_RED) #define is_red(n) ((n) != NULL && (n)->color == RB_RED)
#define swap(n,d,q) ((n)->child[(n)->child[d] == (q)]) #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)); rbnode *n = xmalloc(sizeof(rbnode));
n->key = key; n->key = key;
n->data = data;
n->color = RB_RED; n->color = RB_RED;
n->child[0] = NULL; n->child[0] = NULL;
n->child[1] = NULL; n->child[1] = NULL;
@ -40,7 +39,7 @@ static void node_dealloc(rbnode *n, void (*dealloc)(void *)) {
return; return;
if (dealloc) if (dealloc)
dealloc(n->data); dealloc((void *)n->key);
node_dealloc(n->child[0], dealloc); node_dealloc(n->child[0], dealloc);
node_dealloc(n->child[1], dealloc); node_dealloc(n->child[1], dealloc);
@ -61,22 +60,6 @@ static void rbwalk(rbnode *n, void (*action)(rbnode *)) {
rbwalk(n->child[1], action); 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) { static inline rbnode* rotate_single(rbnode *root, unsigned char dir) {
rbnode *save = root->child[!dir]; rbnode *save = root->child[!dir];
@ -103,34 +86,27 @@ inline int rbtree_is_empty(rbtree *tree) {
/* /*
* Searches a tree by key. * Searches a tree by key.
*/ */
rbnode* rbtree_search(rbtree *tree, unsigned key) { rbnode* rbtree_search(rbtree *tree, const void *key) {
rbnode *n; rbnode *n;
if (tree == NULL || tree->root == NULL) if (tree == NULL || tree->root == NULL || tree->cmp_fn == NULL)
return NULL; return NULL;
n = tree->root; n = tree->root;
while(n) { while(n) {
int cmp = tree->cmp_fn(n->key, key);
if (n->key == key) if (cmp == 0)
break; break;
n = n->child[n->key < key]; n = n->child[cmp < 0];
} }
return n; 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 *)) { void rbtree_walk(rbtree *tree, void (*action)(rbnode *)) {
if (tree == NULL || action == NULL) if (tree == NULL || action == NULL)
@ -148,7 +124,7 @@ void rbtree_free(rbtree *tree) {
tree->root = NULL; tree->root = NULL;
} }
int rbtree_insert(rbtree *tree, unsigned key, void *data) { int rbtree_insert(rbtree *tree, const void *key) {
rbnode head = {0}; rbnode head = {0};
@ -160,8 +136,11 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) {
unsigned char dir = 0, dir2, last, inserted = 0; unsigned char dir = 0, dir2, last, inserted = 0;
if (!tree || !tree->cmp_fn)
return 0;
if (tree->root == NULL) { if (tree->root == NULL) {
tree->root = node_alloc(key, data); tree->root = node_alloc(key);
inserted = 1; inserted = 1;
goto done; goto done;
} }
@ -173,8 +152,10 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) {
/* somewhere in here, there should be dragons */ /* somewhere in here, there should be dragons */
for(;;) { for(;;) {
int cmp;
if (q == NULL) { if (q == NULL) {
p->child[dir] = q = node_alloc(key, data); p->child[dir] = q = node_alloc(key);
inserted = 1; inserted = 1;
} else if (is_red(q->child[0]) && is_red(q->child[1])) { } else if (is_red(q->child[0]) && is_red(q->child[1])) {
/* color flip case */ /* color flip case */
@ -192,11 +173,13 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) {
t->child[dir2] = rotate_double(g, !last); t->child[dir2] = rotate_double(g, !last);
} }
if (q->key == key) cmp = tree->cmp_fn(q->key, key);
if (cmp == 0)
break; break;
last = dir; last = dir;
dir = q->key < key; dir = cmp < 0;
if (g) if (g)
t = g; t = g;
@ -205,11 +188,9 @@ int rbtree_insert(rbtree *tree, unsigned key, void *data) {
} }
if (!inserted) { if (!inserted) {
if (tree->update_fn)
tree->update_fn(q->data, data);
if (tree->delete_fn) if (tree->delete_fn)
tree->delete_fn(q->data); tree->delete_fn((void*)q->key);
q->data = data; q->key = key;
} }
tree->root = head.child[1]; tree->root = head.child[1];
@ -221,7 +202,7 @@ done:
return inserted; return inserted;
} }
int rbtree_delete(rbtree *tree, unsigned key) { int rbtree_delete(rbtree *tree, const void *key) {
rbnode head = {0}; rbnode head = {0};
@ -233,7 +214,7 @@ int rbtree_delete(rbtree *tree, unsigned key) {
unsigned char dir = 1, dir2, last; unsigned char dir = 1, dir2, last;
if (rbtree_is_empty(tree)) if (rbtree_is_empty(tree) || !tree->cmp_fn)
return 0; return 0;
q = &head; q = &head;
@ -243,13 +224,15 @@ int rbtree_delete(rbtree *tree, unsigned key) {
/* more dragons (killed some of them though) */ /* more dragons (killed some of them though) */
while(q->child[dir]) { while(q->child[dir]) {
last = dir; int cmp;
g = p, p = q; g = p, p = q;
q = q->child[dir]; q = q->child[dir];
dir = q->key < key; last = dir;
if (q->key == key) cmp = tree->cmp_fn(q->key, key);
dir = cmp < 0;
if (cmp == 0)
f = q; f = q;
if (is_red(q) || is_red(q->child[dir])) if (is_red(q) || is_red(q->child[dir]))
@ -288,12 +271,10 @@ int rbtree_delete(rbtree *tree, unsigned key) {
/* remove if found */ /* remove if found */
if (f) { if (f) {
if (tree->delete_fn) if (tree->delete_fn)
tree->delete_fn(f->data); tree->delete_fn((void*)f->key);
if (f != q) { if (f != q)
f->key = q->key; f->key = q->key;
f->data = q->data;
}
swap(p, 1, q) = swap(q, 0, NULL); swap(p, 1, q) = swap(q, 0, NULL);
xfree(q); xfree(q);
return 1; return 1;

View file

@ -18,8 +18,7 @@
/* node definition */ /* node definition */
typedef struct _rbn { typedef struct _rbn {
unsigned key; const void *key;
void *data;
struct _rbn *child[2]; struct _rbn *child[2];
unsigned char color; unsigned char color;
} rbnode; } rbnode;
@ -29,24 +28,21 @@ typedef struct {
/* user defined operations */ /* user defined operations */
void (*delete_fn)(void *); void (*delete_fn)(void *);
void (*update_fn)(void *, void *); void (*update_fn)(void *, void *);
/* note: char* is used here to make dereference easier inside the function */ int (*cmp_fn)(const void *, const void *);
int (*cmp_fn)(const char *, const char *);
} rbtree; } rbtree;
#define RBTREE_INIT(delete, update, cmp) { NULL, delete, update, cmp} #define RBTREE_INIT(delete, update, cmp) { NULL, delete, update, cmp}
int rbtree_is_empty(rbtree *tree); int rbtree_is_empty(rbtree *tree);
rbnode* rbtree_search(rbtree *tree, unsigned key); rbnode* rbtree_search(rbtree *tree, const void *key);
rbnode* rbtree_cmp_search(rbtree *tree, const void *data);
void rbtree_walk(rbtree *tree, void (*action)(rbnode *)); void rbtree_walk(rbtree *tree, void (*action)(rbnode *));
void rbtree_free(rbtree *tree); 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 */ #endif /* __RBTREE_H */

View file

@ -16,6 +16,9 @@
#define is_red(n) ((n) != NULL && (n)->color == RB_RED) #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) { static int rb_assert(rbnode *node) {
int rh, lh; int rh, lh;
@ -39,8 +42,8 @@ static int rb_assert(rbnode *node) {
lh = rb_assert(ln); lh = rb_assert(ln);
rh = rb_assert(rn); rh = rb_assert(rn);
if ( (ln != NULL && ln->key >= node->key) && if ( (ln != NULL && vcmp(ln->key, node->key) >= 0) &&
(rn != NULL && rn->key <= node->key) ) { (rn != NULL && vcmp(rn->key, node->key) <= 0) ) {
die("BST violation"); die("BST violation");
return 0; return 0;
} }
@ -58,59 +61,31 @@ static int rb_assert(rbnode *node) {
return 0; return 0;
} }
static int vcmp(const char *a, const char *b);
static void vfree(void *ptr);
static void vupdate(void *old, void *new);
/* data */ /* data */
static rbtree tree = RBTREE_INIT(vfree, vupdate, vcmp); static rbtree tree = RBTREE_INIT(vdelete, NULL, vcmp);
static int keyref[NODES]; 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 *((int*)a) - *((int*)b);
return strcmp(a, b);
} }
static void vupdate(void *old, void *new) { static void vdelete(void *ptr) {
int i; int i;
printf("update: %s -> %s\n", (char*)old, (char*)new); printf("delete: %i\n", *((int*)ptr));
for(i=0; i < NODES; i++) { for(i=0; i < NODES; i++) {
if (keyref[i] == -1) if (keyref[i] == -1)
continue; continue;
if (old == dataref[i]) { if (ptr == &keyref[i]) {
dataref[i] = new;
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; keyref[i] = -1;
dataref[i] = NULL;
break; break;
} }
} }
free(ptr);
} }
static void vwalk(rbnode *node) { static void vwalk(rbnode *node) {
@ -120,46 +95,36 @@ static void vwalk(rbnode *node) {
/* check if this node exist in the reference list */ /* check if this node exist in the reference list */
for(i=0; i < NODES; i++) { for(i=0; i < NODES; i++) {
if (node->key == keyref[i] && node->data == dataref[i]) { if (node->key == &keyref[i]) {
found = 1; found = 1;
break; break;
} }
} }
assert(found); assert(found);
} }
void test_insert() { void test_insert() {
int i = 0, ret; int i = 0;
uint ckey;
char *data;
utest_init_RNG();
/* insert values */ /* insert values */
while(i < NODES) { while(i < NODES) {
ckey = (uint) (rand() % MAX_VAL); rbnode *node;
data = utest_ran_string(16);
keyref[i] = (uint) (rand() % MAX_VAL);
/* insert into rbtree and assert it */ /* insert into rbtree and assert it */
ret = rbtree_insert(&tree, ckey, data); node = rbtree_insert(&tree, &keyref[i]);
rb_assert(tree.root); rb_assert(tree.root);
if (!ret) assert(node);
continue;
printf("insert: %i %s\n", ckey, data); printf("insert: %i\n", *((int*)node->key));
keyref[i] = ckey;
dataref[i] = data;
i++; i++;
} }
/* insert duplicate key */
rbtree_insert(&tree, keyref[rand() % NODES], strdup("---- update ----"));
rb_assert(tree.root); rb_assert(tree.root);
} }
@ -174,8 +139,10 @@ void test_delete() {
key = keyref[rand() % NODES]; key = keyref[rand() % NODES];
while(key < 0); while(key < 0);
rbtree_delete(&tree, key); rbtree_delete(&tree, &key);
rb_assert(tree.root); rb_assert(tree.root);
keyref[key] = -1;
} }
} }
@ -192,40 +159,23 @@ void test_search() {
printf("search: expecting to find key %i\n", keyref[index]); printf("search: expecting to find key %i\n", keyref[index]);
/* search for a key we know exists */ /* search for a key we know exists */
n = rbtree_search(&tree, keyref[index]); n = rbtree_search(&tree, &keyref[index]);
rb_assert(tree.root); rb_assert(tree.root);
assert(n != NULL); assert(n != NULL);
assert(n->key == keyref[index]); assert(n->key == &keyref[index]);
assert_string(n->data, dataref[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 */ /* 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); rb_assert(tree.root);
assert(n == NULL); 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() { void test_walk() {
rbtree_walk(&tree, vwalk); rbtree_walk(&tree, vwalk);
@ -236,19 +186,19 @@ int main(int argc, char **argv) {
tree.root = NULL; tree.root = NULL;
utest_init_RNG();
/* a new tree is empty */ /* a new tree is empty */
assert(rbtree_is_empty(&tree)); assert(rbtree_is_empty(&tree));
test_insert(); test_insert();
test_search(); test_search();
test_cmp_search();
test_walk(); test_walk();
test_delete(); test_delete();
test_search(); test_search();
test_cmp_search();
test_walk(); test_walk();
/* free the tree */ /* free the tree */