Implemented a simlpe red-black tree which should give better performance
for certain lookups. The rb_tree will act as a general purpose key/value storage, and also give a performance boost in the cases where the other simple alternative would be to use a linked_list. On average this should give on average O(log n) lookups, while the linked_list would be O(n) at worst.
This commit is contained in:
parent
5d6184961b
commit
3dcbb63a31
|
@ -54,6 +54,7 @@ extern int exotic_run(struct exotic_handle* handle);
|
|||
#include "test_memory.tcc"
|
||||
#include "test_message.tcc"
|
||||
#include "test_misc.tcc"
|
||||
#include "test_rbtree.tcc"
|
||||
#include "test_sid.tcc"
|
||||
#include "test_tiger.tcc"
|
||||
#include "test_timer.tcc"
|
||||
|
@ -696,6 +697,33 @@ int main(int argc, char** argv)
|
|||
exotic_add_test(&handle, &exotic_test_utf8_valid_10, "utf8_valid_10");
|
||||
exotic_add_test(&handle, &exotic_test_utf8_valid_11, "utf8_valid_11");
|
||||
exotic_add_test(&handle, &exotic_test_utf8_valid_12, "utf8_valid_12");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_create_destroy, "rbtree_create_destroy");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_create_1, "rbtree_create_1");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_size_0, "rbtree_size_0");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_insert_1, "rbtree_insert_1");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_insert_2, "rbtree_insert_2");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_insert_3, "rbtree_insert_3");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_insert_3_again, "rbtree_insert_3_again");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_size_1, "rbtree_size_1");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_1, "rbtree_search_1");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_2, "rbtree_search_2");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_3, "rbtree_search_3");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_4, "rbtree_search_4");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_remove_1, "rbtree_remove_1");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_size_2, "rbtree_size_2");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_remove_2, "rbtree_remove_2");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_remove_3, "rbtree_remove_3");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_remove_3_again, "rbtree_remove_3_again");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_5, "rbtree_search_5");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_6, "rbtree_search_6");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_7, "rbtree_search_7");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_search_8, "rbtree_search_8");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_size_3, "rbtree_size_3");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_insert_10000, "rbtree_insert_10000");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_size_4, "rbtree_size_4");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_check_10000, "rbtree_check_10000");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_iterate_10000, "rbtree_iterate_10000");
|
||||
exotic_add_test(&handle, &exotic_test_rbtree_remove_5000, "rbtree_remove_5000");
|
||||
exotic_add_test(&handle, &exotic_test_sid_create_pool, "sid_create_pool");
|
||||
exotic_add_test(&handle, &exotic_test_sid_check_0a, "sid_check_0a");
|
||||
exotic_add_test(&handle, &exotic_test_sid_check_0b, "sid_check_0b");
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
#include <uhub.h>
|
||||
#include <util/rbtree.h>
|
||||
|
||||
#define MAX_NODES 10000
|
||||
|
||||
static struct rb_tree* tree = NULL;
|
||||
|
||||
int test_tree_compare(const void* a, const void* b)
|
||||
{
|
||||
return strcmp((const char*) a, (const char*) b);
|
||||
}
|
||||
|
||||
|
||||
EXO_TEST(rbtree_create_destroy, {
|
||||
int ok = 0;
|
||||
struct rb_tree* atree;
|
||||
atree = rb_tree_create(test_tree_compare, &hub_malloc, &hub_free);
|
||||
if (atree) ok = 1;
|
||||
rb_tree_destroy(atree);
|
||||
return ok;
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_create_1, {
|
||||
tree = rb_tree_create(test_tree_compare, &hub_malloc, &hub_free);
|
||||
return tree != NULL;
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_size_0, { return rb_tree_size(tree) == 0; });
|
||||
|
||||
EXO_TEST(rbtree_insert_1, {
|
||||
return rb_tree_insert(tree, "one", "1");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_insert_2, {
|
||||
return rb_tree_insert(tree, "two", "2");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_insert_3, {
|
||||
return rb_tree_insert(tree, "three", "3");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_insert_3_again, {
|
||||
return !rb_tree_insert(tree, "three", "3-again");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_size_1, { return rb_tree_size(tree) == 3; });
|
||||
|
||||
static int test_check_search(const char* key, const char* expect)
|
||||
{
|
||||
const char* value = (const char*) rb_tree_get(tree, key);
|
||||
if (!value) return !expect;
|
||||
if (!expect) return 0;
|
||||
return strcmp(value, expect) == 0;
|
||||
}
|
||||
|
||||
EXO_TEST(rbtree_search_1, { return test_check_search("one", "1"); });
|
||||
EXO_TEST(rbtree_search_2, { return test_check_search("two", "2"); });
|
||||
EXO_TEST(rbtree_search_3, { return test_check_search("three", "3"); });
|
||||
EXO_TEST(rbtree_search_4, { return test_check_search("four", NULL); });
|
||||
|
||||
|
||||
EXO_TEST(rbtree_remove_1, {
|
||||
return rb_tree_remove(tree, "one");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_size_2, { return rb_tree_size(tree) == 2; });
|
||||
|
||||
EXO_TEST(rbtree_remove_2, {
|
||||
return rb_tree_remove(tree, "two");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_remove_3, {
|
||||
return rb_tree_remove(tree, "three");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_remove_3_again, {
|
||||
return !rb_tree_remove(tree, "three");
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_search_5, { return test_check_search("one", NULL); });
|
||||
EXO_TEST(rbtree_search_6, { return test_check_search("two", NULL); });
|
||||
EXO_TEST(rbtree_search_7, { return test_check_search("three", NULL); });
|
||||
EXO_TEST(rbtree_search_8, { return test_check_search("four", NULL); });
|
||||
|
||||
EXO_TEST(rbtree_size_3, { return rb_tree_size(tree) == 0; });
|
||||
|
||||
EXO_TEST(rbtree_insert_10000, {
|
||||
int i;
|
||||
for (i = 0; i < MAX_NODES ; i++)
|
||||
{
|
||||
const char* key = strdup(uhub_itoa(i));
|
||||
const char* val = strdup(uhub_itoa(i + 16384));
|
||||
if (!rb_tree_insert(tree, key, val))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_size_4, { return rb_tree_size(tree) == MAX_NODES ; });
|
||||
|
||||
EXO_TEST(rbtree_check_10000, {
|
||||
int i;
|
||||
for (i = 0; i < MAX_NODES ; i++)
|
||||
{
|
||||
char* key = strdup(uhub_itoa(i));
|
||||
const char* expect = uhub_itoa(i + 16384);
|
||||
if (!test_check_search(key, expect))
|
||||
return 0;
|
||||
hub_free(key);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
|
||||
EXO_TEST(rbtree_iterate_10000, {
|
||||
int i = 0;
|
||||
struct rb_node* n = (struct rb_node*) rb_tree_first(tree);
|
||||
while (n)
|
||||
{
|
||||
struct rb_node* p = n;
|
||||
n = (struct rb_node*) rb_tree_next(tree);
|
||||
i++;
|
||||
}
|
||||
return i == MAX_NODES ;
|
||||
});
|
||||
|
||||
|
||||
static void free_node(struct rb_node* n)
|
||||
{
|
||||
hub_free(n->key);
|
||||
hub_free(n->value);
|
||||
}
|
||||
|
||||
EXO_TEST(rbtree_remove_5000, {
|
||||
int i = 0;
|
||||
struct rb_node* n = (struct rb_node*) rb_tree_first(tree);
|
||||
for (i = 0; i < MAX_NODES ; i += 2)
|
||||
{
|
||||
const char* key = uhub_itoa(i);
|
||||
rb_tree_remove_node(tree, key, &free_node);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2009, Jan Vidar Krey
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,84 +17,140 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "uhub.h"
|
||||
#include "rbtree.h"
|
||||
|
||||
#define RED 0
|
||||
#define BLACK 1
|
||||
|
||||
struct rb_node
|
||||
{
|
||||
const void* key;
|
||||
const void* value; /* data */
|
||||
int color;
|
||||
struct rb_node* parent;
|
||||
struct rb_node* left;
|
||||
struct rb_node* right;
|
||||
};
|
||||
|
||||
struct rb_tree
|
||||
{
|
||||
struct rb_node* root;
|
||||
size_t elements;
|
||||
rb_tree_alloc alloc;
|
||||
rb_tree_free free;
|
||||
rb_tree_compare compare;
|
||||
};
|
||||
|
||||
/* returns the grandparent of a node, if it exits */
|
||||
static inline struct rb_node* get_grandparent(struct rb_node* n)
|
||||
{
|
||||
if (n->parent)
|
||||
return n->parent->parent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rb_node* get_uncle(struct rb_node* n)
|
||||
{
|
||||
struct rb_node* gparent = n->parent ? n->parent->parent : 0;
|
||||
if (gparent)
|
||||
return (n->parent == gparent->left) ? gparent->right : gparent->left;
|
||||
return 0;
|
||||
}
|
||||
// #define RB_TREE_CHECKS
|
||||
|
||||
static struct rb_node* tree_search(struct rb_tree* tree, const void* key)
|
||||
{
|
||||
struct rb_node* node = tree->root;
|
||||
while (node)
|
||||
{
|
||||
int res = tree->compare(key, node->key);
|
||||
if (res < 0) node = node->left;
|
||||
else if (res > 0) node = node->right;
|
||||
else return node;
|
||||
int res = tree->compare(node->key, key);
|
||||
if (!res)
|
||||
break;
|
||||
node = node->link[res < 0];
|
||||
}
|
||||
return 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
static struct rb_node* tree_insert(struct rb_tree* tree, const void* key, const void* value)
|
||||
static struct rb_node* create_node(struct rb_tree* tree, const void* key, const void* value)
|
||||
{
|
||||
struct rb_node* node = tree->root;
|
||||
struct rb_node* newnode = tree->alloc(sizeof(struct rb_node));
|
||||
newnode->key = key;
|
||||
newnode->value = value;
|
||||
newnode->color = RED;
|
||||
|
||||
struct rb_node* node = tree->alloc(sizeof(struct rb_node));
|
||||
node->key = key;
|
||||
node->value = value;
|
||||
node->red = 1;
|
||||
node->link[0] = 0;
|
||||
node->link[1] = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
while (node)
|
||||
static int is_red(struct rb_node* node)
|
||||
{
|
||||
return node && node->red;
|
||||
}
|
||||
|
||||
#ifdef RB_TREE_CHECKS
|
||||
int rb_tree_check(struct rb_tree* tree, struct rb_node* node)
|
||||
{
|
||||
int lh, rh;
|
||||
|
||||
if (node == NULL)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
int res = tree->compare(key, node->key);
|
||||
if (res < 0) node = node->left;
|
||||
else if (res > 0) node = node->right;
|
||||
else
|
||||
struct rb_node *ln = node->link[0];
|
||||
struct rb_node *rn = node->link[1];
|
||||
|
||||
/* Consecutive red links */
|
||||
if (is_red(node)) {
|
||||
if (is_red(ln) || is_red(rn))
|
||||
{
|
||||
puts("Red violation");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
lh = rb_tree_check(tree, ln);
|
||||
rh = rb_tree_check(tree, rn);
|
||||
|
||||
/* Invalid binary search tree - not sorted correctly */
|
||||
if ((ln && tree->compare(ln->key, node->key) >= 0) || (rn && tree->compare(rn->key, node->key) <= 0))
|
||||
{
|
||||
/* key already exists in tree */
|
||||
return node;
|
||||
puts("Binary tree violation");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Black height mismatch */
|
||||
if ( lh != 0 && rh != 0 && lh != rh ) {
|
||||
puts ( "Black violation" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only count black links */
|
||||
if (lh != 0 && rh != 0)
|
||||
return is_red(node) ? lh : lh + 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif // RB_TREE_CHECKS
|
||||
|
||||
static struct rb_node* rb_tree_rotate_single(struct rb_node* node, int dir)
|
||||
{
|
||||
struct rb_node* other = node->link[!dir];
|
||||
|
||||
node->link[!dir] = other->link[dir];
|
||||
other->link[dir] = node;
|
||||
|
||||
node->red = 1;
|
||||
other->red = 0;
|
||||
return other;
|
||||
}
|
||||
|
||||
static struct rb_node* rb_tree_rotate_double(struct rb_node* node, int dir)
|
||||
{
|
||||
node->link[!dir] = rb_tree_rotate_single(node->link[!dir], !dir);
|
||||
return rb_tree_rotate_single(node, dir);
|
||||
}
|
||||
|
||||
static struct rb_node* rb_tree_insert_r(struct rb_tree* tree, struct rb_node* node, const void* key, const void* value)
|
||||
{
|
||||
if (!node)
|
||||
return create_node(tree, key, value);
|
||||
|
||||
int res = tree->compare(node->key, key);
|
||||
if (!res)
|
||||
{
|
||||
puts("Node already exists!");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
int dir = res < 0;
|
||||
node->link[dir] = rb_tree_insert_r(tree, node->link[dir], key, value);
|
||||
|
||||
if (is_red(node->link[dir]))
|
||||
{
|
||||
if (is_red(node->link[!dir]))
|
||||
{
|
||||
/* Case 1 */
|
||||
node->red = 1;
|
||||
node->link[0]->red = 0;
|
||||
node->link[1]->red = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Cases 2 & 3 */
|
||||
if (is_red(node->link[dir]->link[dir]))
|
||||
node = rb_tree_rotate_single(node, !dir);
|
||||
else if (is_red(node->link[dir]->link[!dir]))
|
||||
node = rb_tree_rotate_double(node, !dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newnode;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,36 +158,224 @@ struct rb_tree* rb_tree_create(rb_tree_compare compare, rb_tree_alloc a, rb_tree
|
|||
{
|
||||
struct rb_tree* tree = a(sizeof(struct rb_tree));
|
||||
tree->compare = compare;
|
||||
tree->alloc = a;
|
||||
tree->free = f;
|
||||
tree->alloc = a ? a : hub_malloc;
|
||||
tree->free = f ? f : hub_free;
|
||||
tree->root = NULL;
|
||||
tree->elements = 0;
|
||||
tree->iterator.node = NULL;
|
||||
tree->iterator.stack = list_create();
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
void rb_tree_destroy(struct rb_tree* tree)
|
||||
{
|
||||
list_destroy(tree->iterator.stack);
|
||||
rb_tree_free f = tree->free;
|
||||
f(tree);
|
||||
}
|
||||
|
||||
void* rb_tree_insert(struct rb_tree* tree, const void* key, const void* value)
|
||||
int rb_tree_insert(struct rb_tree* tree, const void* key, const void* value)
|
||||
{
|
||||
struct rb_node* node = tree_insert(tree, key, value);
|
||||
if (node)
|
||||
return (void*) node->value;
|
||||
return 0;
|
||||
if (tree_search(tree, key))
|
||||
return 0;
|
||||
struct rb_node* node = rb_tree_insert_r(tree, tree->root, key, value);
|
||||
tree->root = node;
|
||||
tree->root->red = 0;
|
||||
tree->elements++;
|
||||
#ifdef RB_TREE_CHECKS
|
||||
rb_tree_check(tree, node);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* rb_tree_remove(struct rb_tree* tree, const void* key)
|
||||
{
|
||||
void null_node_free(struct rb_node* n) { }
|
||||
|
||||
int rb_tree_remove(struct rb_tree* tree, const void* key)
|
||||
{
|
||||
return rb_tree_remove_node(tree, key, &null_node_free);
|
||||
}
|
||||
|
||||
int rb_tree_remove_node(struct rb_tree* tree, const void* key, rb_tree_free_node freecb)
|
||||
{
|
||||
struct rb_node head = {0}; /* False tree root */
|
||||
struct rb_node *q, *p, *g; /* Helpers */
|
||||
struct rb_node *f = NULL; /* Found item */
|
||||
int dir = 1;
|
||||
|
||||
if (!tree->root)
|
||||
return 0;
|
||||
|
||||
/* Set up helpers */
|
||||
q = &head;
|
||||
g = p = NULL;
|
||||
q->link[1] = tree->root;
|
||||
|
||||
/* Search and push a red down */
|
||||
while (q->link[dir])
|
||||
{
|
||||
int last = dir;
|
||||
int res;
|
||||
|
||||
/* Update helpers */
|
||||
g = p, p = q;
|
||||
q = q->link[dir];
|
||||
res = tree->compare(q->key, key);
|
||||
dir = res < 0;
|
||||
|
||||
/* Save found node */
|
||||
if (!res)
|
||||
f = q;
|
||||
|
||||
/* Push the red node down */
|
||||
if (!is_red(q) && !is_red(q->link[dir]))
|
||||
{
|
||||
if (is_red(q->link[!dir]))
|
||||
p = p->link[last] = rb_tree_rotate_single(q, dir);
|
||||
else if (!is_red(q->link[!dir]))
|
||||
{
|
||||
struct rb_node* s = p->link[!last];
|
||||
if (s)
|
||||
{
|
||||
if (!is_red(s->link[!last]) && !is_red (s->link[last]))
|
||||
{
|
||||
/* Color flip */
|
||||
p->red = 0;
|
||||
s->red = 1;
|
||||
q->red = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int dir2 = g->link[1] == p;
|
||||
if (is_red(s->link[last]))
|
||||
g->link[dir2] = rb_tree_rotate_double(p, last);
|
||||
else if (is_red(s->link[!last]))
|
||||
g->link[dir2] = rb_tree_rotate_single(p, last);
|
||||
|
||||
/* Ensure correct coloring */
|
||||
q->red = g->link[dir2]->red = 1;
|
||||
g->link[dir2]->link[0]->red = 0;
|
||||
g->link[dir2]->link[1]->red = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace and remove if found */
|
||||
if (f)
|
||||
{
|
||||
freecb(f);
|
||||
f->key = q->key;
|
||||
f->value = q->value;
|
||||
p->link[p->link[1] == q] = q->link[q->link[0] == NULL];
|
||||
tree->free(q);
|
||||
tree->elements--;
|
||||
}
|
||||
|
||||
/* Update root and make it black */
|
||||
tree->root = head.link[1];
|
||||
if (tree->root != NULL)
|
||||
tree->root->red = 0;
|
||||
|
||||
#ifdef RB_TREE_CHECKS
|
||||
rb_tree_check(tree, tree->root);
|
||||
#endif
|
||||
|
||||
return f != NULL;
|
||||
}
|
||||
|
||||
void* rb_tree_get(struct rb_tree* tree, const void* key)
|
||||
{
|
||||
struct rb_node* node = tree_search(tree, key);
|
||||
if (node)
|
||||
return node->value;
|
||||
return (void*) node->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
size_t rb_tree_size(struct rb_tree* tree)
|
||||
{
|
||||
return tree->elements;
|
||||
}
|
||||
|
||||
static void push(struct rb_tree* tree, struct rb_node* n)
|
||||
{
|
||||
list_append(tree->iterator.stack, n);
|
||||
}
|
||||
|
||||
static struct rb_node* pop(struct rb_tree* tree)
|
||||
{
|
||||
struct rb_node* n = list_get_last(tree->iterator.stack);
|
||||
if (n)
|
||||
list_remove(tree->iterator.stack, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rb_node* rb_it_set(struct rb_tree* tree, struct rb_node* n)
|
||||
{
|
||||
tree->iterator.node = n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void null_free(void* ptr) { }
|
||||
|
||||
struct rb_node* rb_tree_first(struct rb_tree* tree)
|
||||
{
|
||||
struct rb_node* n = tree->root;
|
||||
list_clear(tree->iterator.stack, &null_free);
|
||||
while (n->link[0])
|
||||
{
|
||||
push(tree, n);
|
||||
n = n->link[0];
|
||||
}
|
||||
return rb_it_set(tree, n);
|
||||
};
|
||||
|
||||
|
||||
static struct rb_node* rb_tree_traverse(struct rb_tree* tree, int dir)
|
||||
{
|
||||
struct rb_node* n = tree->iterator.node;
|
||||
struct rb_node* p; /* parent */
|
||||
|
||||
if (n->link[dir])
|
||||
{
|
||||
push(tree, n);
|
||||
n = n->link[dir];
|
||||
while (n->link[!dir])
|
||||
{
|
||||
list_append(tree->iterator.stack, n);
|
||||
n = n->link[!dir];
|
||||
}
|
||||
return rb_it_set(tree, n);
|
||||
}
|
||||
|
||||
// Need to walk upwards to the parent node.
|
||||
p = pop(tree);
|
||||
if (p)
|
||||
{
|
||||
// walk up in opposite direction
|
||||
if (p->link[!dir] == n)
|
||||
return rb_it_set(tree, p);
|
||||
|
||||
// walk up in hte current direction
|
||||
while (p->link[dir] == n)
|
||||
{
|
||||
n = p;
|
||||
p = pop(tree);
|
||||
if (!p)
|
||||
return rb_it_set(tree, NULL);
|
||||
}
|
||||
return rb_it_set(tree, p);
|
||||
}
|
||||
return rb_it_set(tree, NULL);
|
||||
}
|
||||
|
||||
struct rb_node* rb_tree_next(struct rb_tree* tree)
|
||||
{
|
||||
return rb_tree_traverse(tree, 1);
|
||||
}
|
||||
|
||||
struct rb_node* rb_tree_prev(struct rb_tree* tree)
|
||||
{
|
||||
return rb_tree_traverse(tree, 0);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2009, Jan Vidar Krey
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,18 +20,121 @@
|
|||
#ifndef HAVE_UHUB_RED_BLACK_TREE_H
|
||||
#define HAVE_UHUB_RED_BLACK_TREE_H
|
||||
|
||||
struct rb_tree;
|
||||
struct rb_node
|
||||
{
|
||||
const void* key;
|
||||
const void* value; /* data */
|
||||
int red;
|
||||
struct rb_node* link[2];
|
||||
};
|
||||
|
||||
typedef int (*rb_tree_compare)(const void* a, const void* b);
|
||||
typedef void* (*rb_tree_alloc)(size_t);
|
||||
typedef void (*rb_tree_free)(void*);
|
||||
typedef void (*rb_tree_free_node)(struct rb_node*);
|
||||
|
||||
struct rb_iterator
|
||||
{
|
||||
struct rb_node* node; // current node.
|
||||
struct linked_list* stack; // stack from the top -- needed since we don't have parent pointers.
|
||||
};
|
||||
|
||||
struct rb_tree
|
||||
{
|
||||
struct rb_node* root;
|
||||
size_t elements;
|
||||
rb_tree_alloc alloc;
|
||||
rb_tree_free free;
|
||||
rb_tree_compare compare;
|
||||
struct rb_iterator iterator;
|
||||
};
|
||||
|
||||
|
||||
extern struct rb_tree* rb_tree_create(rb_tree_compare, rb_tree_alloc, rb_tree_free);
|
||||
|
||||
/**
|
||||
* Create a tree.
|
||||
*
|
||||
* @param compare Comparison function
|
||||
* @param alloc Allocator (if NULL then hub_malloc() is used)
|
||||
* @param dealloc Deallocator (if NULL then hub_free() is used)
|
||||
* @return a tree handle.
|
||||
*/
|
||||
extern struct rb_tree* rb_tree_create(rb_tree_compare compare, rb_tree_alloc alloc, rb_tree_free dealloc);
|
||||
|
||||
/**
|
||||
* Deletes the tree and all the nodes.
|
||||
* But not the content inside the nodes.
|
||||
*/
|
||||
extern void rb_tree_destroy(struct rb_tree*);
|
||||
|
||||
extern void* rb_tree_insert(struct rb_tree* tree, const void* key, const void* data);
|
||||
extern void* rb_tree_remove(struct rb_tree* tree, const void* key);
|
||||
extern void* rb_tree_get(struct rb_tree* tree, const void* key);
|
||||
/**
|
||||
* Insert a key into the tree, returns 1 if successful,
|
||||
* or 0 if the key already existed.
|
||||
*/
|
||||
extern int rb_tree_insert(struct rb_tree* tree, const void* key, const void* data);
|
||||
|
||||
/**
|
||||
* Remove a key from the tree.
|
||||
* Returns 1 if the node was removed, or 0 if it was not removed (i.e. not found!)
|
||||
*
|
||||
* NOTE: the content of the node is not freed if it needs to be then use rb_tree_remove_node
|
||||
* where you can specify a callback cleanup function.
|
||||
*/
|
||||
extern int rb_tree_remove(struct rb_tree* tree, const void* key);
|
||||
|
||||
/**
|
||||
* Remove the node, but call the free callback before the node is removed.
|
||||
* This is useful in cases where you need to deallocate the key and/or value from the node.
|
||||
* Returns 1 if the node was removed, or 0 if not found.
|
||||
*/
|
||||
extern int rb_tree_remove_node(struct rb_tree* tree, const void* key, rb_tree_free_node free);
|
||||
|
||||
/**
|
||||
* Returns NULL if the key was not found in the tree.
|
||||
*/
|
||||
extern void* rb_tree_get(struct rb_tree* tree, const void* key);
|
||||
|
||||
/**
|
||||
* Returns the number of elements inside the tree.
|
||||
*/
|
||||
extern size_t rb_tree_size(struct rb_tree* tree);
|
||||
|
||||
/**
|
||||
* Returns the first node in the tree.
|
||||
* (leftmost, or lowest value in sorted order).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <code>
|
||||
* struct rb_node* it;
|
||||
* for (it = rb_tree_first(tree); it; it = rb_tree_next())
|
||||
* {
|
||||
* void* key = rb_iterator_key(it);
|
||||
* void* value = rb_iterator_value(it);
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
extern struct rb_node* rb_tree_first(struct rb_tree* tree);
|
||||
|
||||
/**
|
||||
* Points the iterator at the next node.
|
||||
* If the next node is NULL then the iterator becomes NULL too.
|
||||
*/
|
||||
extern struct rb_node* rb_tree_next(struct rb_tree* tree);
|
||||
extern struct rb_node* rb_tree_prev(struct rb_tree* tree);
|
||||
|
||||
/**
|
||||
* Returnst the key of the node pointed to by the iterator.
|
||||
* If this iterator is the same as rb_tree_end() then NULL is returned.
|
||||
*/
|
||||
extern void* rb_iterator_key(struct rb_iterator* it);
|
||||
|
||||
/**
|
||||
* Returnst the value of the node pointed to by the iterator.
|
||||
* If this iterator is the same as rb_tree_end() then the behavior is undefined.
|
||||
*/
|
||||
extern void* rb_iterator_value(struct rb_iterator* it);
|
||||
|
||||
|
||||
|
||||
#endif /* HAVE_UHUB_RED_BLACK_TREE_H */
|
||||
|
|
Loading…
Reference in New Issue