/*
a talloc based red-black tree
Copyright (C) Ronnie Sahlberg 2007
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
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see .
*/
#include "includes.h"
#include "rb_tree.h"
#define NO_MEMORY_FATAL(p) do { if (!(p)) { \
DEBUG(0,("Out of memory for %s at %s\n", #p, __location__)); \
exit(10); \
}} while (0)
trbt_tree_t *
trbt_create(TALLOC_CTX *memctx)
{
trbt_tree_t *tree;
tree = talloc_zero(memctx, trbt_tree_t);
NO_MEMORY_FATAL(tree);
return tree;
}
static inline trbt_node_t *
trbt_parent(trbt_node_t *node)
{
return node->parent;
}
static inline trbt_node_t *
trbt_grandparent(trbt_node_t *node)
{
trbt_node_t *parent;
parent=trbt_parent(node);
if(parent){
return parent->parent;
}
return NULL;
}
static inline trbt_node_t *
trbt_uncle(trbt_node_t *node)
{
trbt_node_t *parent, *grandparent;
parent=trbt_parent(node);
if(!parent){
return NULL;
}
grandparent=trbt_parent(parent);
if(!grandparent){
return NULL;
}
if(parent==grandparent->left){
return grandparent->right;
}
return grandparent->left;
}
static inline void trbt_insert_case1(trbt_tree_t *tree, trbt_node_t *node);
static inline void trbt_insert_case2(trbt_tree_t *tree, trbt_node_t *node);
static inline void
trbt_rotate_left(trbt_node_t *node)
{
trbt_tree_t *tree = node->tree;
if(node->parent){
if(node->parent->left==node){
node->parent->left=node->right;
} else {
node->parent->right=node->right;
}
} else {
tree->tree=node->right;
}
node->right->parent=node->parent;
node->parent=node->right;
node->right=node->right->left;
if(node->right){
node->right->parent=node;
}
node->parent->left=node;
}
static inline void
trbt_rotate_right(trbt_node_t *node)
{
trbt_tree_t *tree = node->tree;
if(node->parent){
if(node->parent->left==node){
node->parent->left=node->left;
} else {
node->parent->right=node->left;
}
} else {
tree->tree=node->left;
}
node->left->parent=node->parent;
node->parent=node->left;
node->left=node->left->right;
if(node->left){
node->left->parent=node;
}
node->parent->right=node;
}
/* NULL nodes are black by definition */
static inline int trbt_get_color(trbt_node_t *node)
{
if (node==NULL) {
return TRBT_BLACK;
}
return node->rb_color;
}
static inline int trbt_get_color_left(trbt_node_t *node)
{
if (node==NULL) {
return TRBT_BLACK;
}
if (node->left==NULL) {
return TRBT_BLACK;
}
return node->left->rb_color;
}
static inline int trbt_get_color_right(trbt_node_t *node)
{
if (node==NULL) {
return TRBT_BLACK;
}
if (node->right==NULL) {
return TRBT_BLACK;
}
return node->right->rb_color;
}
/* setting a NULL node to black is a nop */
static inline void trbt_set_color(trbt_node_t *node, int color)
{
if ( (node==NULL) && (color==TRBT_BLACK) ) {
return;
}
node->rb_color = color;
}
static inline void trbt_set_color_left(trbt_node_t *node, int color)
{
if ( ((node==NULL)||(node->left==NULL)) && (color==TRBT_BLACK) ) {
return;
}
node->left->rb_color = color;
}
static inline void trbt_set_color_right(trbt_node_t *node, int color)
{
if ( ((node==NULL)||(node->right==NULL)) && (color==TRBT_BLACK) ) {
return;
}
node->right->rb_color = color;
}
static inline void
trbt_insert_case5(trbt_tree_t *tree, trbt_node_t *node)
{
trbt_node_t *grandparent;
trbt_node_t *parent;
parent=trbt_parent(node);
grandparent=trbt_parent(parent);
parent->rb_color=TRBT_BLACK;
grandparent->rb_color=TRBT_RED;
if( (node==parent->left) && (parent==grandparent->left) ){
trbt_rotate_right(grandparent);
} else {
trbt_rotate_left(grandparent);
}
}
static inline void
trbt_insert_case4(trbt_tree_t *tree, trbt_node_t *node)
{
trbt_node_t *grandparent;
trbt_node_t *parent;
parent=trbt_parent(node);
grandparent=trbt_parent(parent);
if(!grandparent){
return;
}
if( (node==parent->right) && (parent==grandparent->left) ){
trbt_rotate_left(parent);
node=node->left;
} else if( (node==parent->left) && (parent==grandparent->right) ){
trbt_rotate_right(parent);
node=node->right;
}
trbt_insert_case5(tree, node);
}
static inline void
trbt_insert_case3(trbt_tree_t *tree, trbt_node_t *node)
{
trbt_node_t *grandparent;
trbt_node_t *parent;
trbt_node_t *uncle;
uncle=trbt_uncle(node);
if(uncle && (uncle->rb_color==TRBT_RED)){
parent=trbt_parent(node);
parent->rb_color=TRBT_BLACK;
uncle->rb_color=TRBT_BLACK;
grandparent=trbt_grandparent(node);
grandparent->rb_color=TRBT_RED;
trbt_insert_case1(tree, grandparent);
} else {
trbt_insert_case4(tree, node);
}
}
static inline void
trbt_insert_case2(trbt_tree_t *tree, trbt_node_t *node)
{
trbt_node_t *parent;
parent=trbt_parent(node);
/* parent is always non-NULL here */
if(parent->rb_color==TRBT_BLACK){
return;
}
trbt_insert_case3(tree, node);
}
static inline void
trbt_insert_case1(trbt_tree_t *tree, trbt_node_t *node)
{
trbt_node_t *parent;
parent=trbt_parent(node);
if(!parent){
node->rb_color=TRBT_BLACK;
return;
}
trbt_insert_case2(tree, node);
}
static inline trbt_node_t *
trbt_sibling(trbt_node_t *node)
{
trbt_node_t *parent;
parent=trbt_parent(node);
if(!parent){
return NULL;
}
if (node == parent->left) {
return parent->right;
} else {
return parent->left;
}
}
static inline trbt_node_t *
trbt_sibline(trbt_node_t *node)
{
if (node==node->parent->left) {
return node->parent->right;
} else {
return node->parent->left;
}
}
static inline void
trbt_delete_case6(trbt_node_t *node)
{
trbt_node_t *sibling, *parent;
sibling = trbt_sibling(node);
parent = trbt_parent(node);
trbt_set_color(sibling, parent->rb_color);
trbt_set_color(parent, TRBT_BLACK);
if (node == parent->left) {
trbt_set_color_right(sibling, TRBT_BLACK);
trbt_rotate_left(parent);
} else {
trbt_set_color_left(sibling, TRBT_BLACK);
trbt_rotate_right(parent);
}
}
static inline void
trbt_delete_case5(trbt_node_t *node)
{
trbt_node_t *parent, *sibling;
parent = trbt_parent(node);
sibling = trbt_sibling(node);
if ( (node == parent->left)
&&(trbt_get_color(sibling) == TRBT_BLACK)
&&(trbt_get_color_left(sibling) == TRBT_RED)
&&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
trbt_set_color(sibling, TRBT_RED);
trbt_set_color_left(sibling, TRBT_BLACK);
trbt_rotate_right(sibling);
trbt_delete_case6(node);
return;
}
if ( (node == parent->right)
&&(trbt_get_color(sibling) == TRBT_BLACK)
&&(trbt_get_color_right(sibling) == TRBT_RED)
&&(trbt_get_color_left(sibling) == TRBT_BLACK) ){
trbt_set_color(sibling, TRBT_RED);
trbt_set_color_right(sibling, TRBT_BLACK);
trbt_rotate_left(sibling);
trbt_delete_case6(node);
return;
}
trbt_delete_case6(node);
}
static inline void
trbt_delete_case4(trbt_node_t *node)
{
trbt_node_t *sibling;
sibling = trbt_sibling(node);
if ( (trbt_get_color(node->parent) == TRBT_RED)
&&(trbt_get_color(sibling) == TRBT_BLACK)
&&(trbt_get_color_left(sibling) == TRBT_BLACK)
&&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
trbt_set_color(sibling, TRBT_RED);
trbt_set_color(node->parent, TRBT_BLACK);
} else {
trbt_delete_case5(node);
}
}
static void trbt_delete_case1(trbt_node_t *node);
static inline void
trbt_delete_case3(trbt_node_t *node)
{
trbt_node_t *sibling;
sibling = trbt_sibling(node);
if ( (trbt_get_color(node->parent) == TRBT_BLACK)
&&(trbt_get_color(sibling) == TRBT_BLACK)
&&(trbt_get_color_left(sibling) == TRBT_BLACK)
&&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
trbt_set_color(sibling, TRBT_RED);
trbt_delete_case1(node->parent);
} else {
trbt_delete_case4(node);
}
}
static inline void
trbt_delete_case2(trbt_node_t *node)
{
trbt_node_t *sibling;
sibling = trbt_sibling(node);
if (trbt_get_color(sibling) == TRBT_RED) {
trbt_set_color(node->parent, TRBT_RED);
trbt_set_color(sibling, TRBT_BLACK);
if (node == node->parent->left) {
trbt_rotate_left(node->parent);
} else {
trbt_rotate_right(node->parent);
}
}
trbt_delete_case3(node);
}
static void
trbt_delete_case1(trbt_node_t *node)
{
if (!node->parent) {
return;
} else {
trbt_delete_case2(node);
}
}
static void
delete_node(trbt_node_t *node)
{
trbt_node_t *parent, *child, dc;
/* This node has two child nodes, then just copy the content
from the next smaller node with this node and delete the
predecessor instead.
The predecessor is guaranteed to have at most one child
node since its right arm must be NULL
(It must be NULL since we are its sucessor and we are above
it in the tree)
*/
if (node->left != NULL && node->right != NULL) {
/* This node has two children, just copy the data */
/* find the predecessor */
trbt_node_t *temp = node->left;
while (temp->right != NULL) {
temp = temp->right;
}
/* swap the predecessor data and key with the node to
be deleted.
*/
talloc_free(node->data);
node->data = talloc_steal(node, temp->data);
node->key32 = temp->key32;
temp->data = NULL;
temp->key32 = -1;
/* then delete the temp node.
this node is guaranteed to have at least one leaf child */
delete_node(temp);
return;
}
/* There is at most one child to this node to be deleted */
child = node->left;
if (node->right) {
child = node->right;
}
/* If the node to be deleted did not have any child at all we
create a temporary dummy node for the child and mark it black.
Once the delete of the node is finished, we remove this dummy
node, which is simple to do since it is guaranteed that it will
still not have any children after the delete operation.
This is because we dont represent the leaf-nodes as actual nodes
in this implementation.
*/
if (!child) {
child = &dc;
child->tree = node->tree;
child->left=NULL;
child->right=NULL;
child->rb_color=TRBT_BLACK;
child->data=NULL;
}
/* replace node with child */
parent = trbt_parent(node);
if (parent) {
if (parent->left == node) {
parent->left = child;
} else {
parent->right = child;
}
} else {
node->tree->tree = child;
}
child->parent = node->parent;
if (node->rb_color == TRBT_BLACK) {
if (trbt_get_color(child) == TRBT_RED) {
child->rb_color = TRBT_BLACK;
} else {
trbt_delete_case1(child);
}
}
/* If we had to create a temporary dummy node to represent a black
leaf child we now has to delete it.
This is simple since this dummy node originally had no children
and we are guaranteed that it will also not have any children
after the node has been deleted and any possible rotations
have occured.
The only special case is if this was the last node of the tree
in which case we have to reset the root to NULL as well.
Othervise it is enough to just unlink the child from its new
parent.
*/
if (child == &dc) {
if (child->parent == NULL) {
node->tree->tree = NULL;
} else if (child == child->parent->left) {
child->parent->left = NULL;
} else {
child->parent->right = NULL;
}
}
talloc_free(node);
return;
}
static inline trbt_node_t *
trbt_create_node(trbt_tree_t *tree, trbt_node_t *parent, uint32_t key, void *data)
{
trbt_node_t *node;
node=talloc_zero(tree, trbt_node_t);
NO_MEMORY_FATAL(node);
node->tree=tree;
node->rb_color=TRBT_BLACK;
node->parent=parent;
node->left=NULL;
node->right=NULL;
node->key32=key;
node->data=talloc_steal(node, data);
return node;
}
/* insert a new node in the tree.
if there is already a node with a matching key in the tree
we reurn an error
*/
int
trbt_insert32(trbt_tree_t *tree, uint32_t key, void *data)
{
trbt_node_t *node;
node=tree->tree;
/* is this the first node ?*/
if(!node){
node = trbt_create_node(tree, NULL, key, data);
tree->tree=node;
return 0;
}
/* it was not the new root so walk the tree until we find where to
* insert this new leaf.
*/
while(1){
/* this node already exists, so just return an error */
if(key==node->key32){
return -1;
}
if(keykey32) {
if(!node->left){
/* new node to the left */
trbt_node_t *new_node;
new_node = trbt_create_node(tree, node, key, data);
node->left=new_node;
node=new_node;
break;
}
node=node->left;
continue;
}
if(key>node->key32) {
if(!node->right){
/* new node to the right */
trbt_node_t *new_node;
new_node = trbt_create_node(tree, node, key, data);
node->right=new_node;
node=new_node;
break;
}
node=node->right;
continue;
}
}
/* node will now point to the newly created node */
node->rb_color=TRBT_RED;
trbt_insert_case1(tree, node);
return 0;
}
void *
trbt_lookup32(trbt_tree_t *tree, uint32_t key)
{
trbt_node_t *node;
node=tree->tree;
while(node){
if(key==node->key32){
return node->data;
}
if(keykey32){
node=node->left;
continue;
}
if(key>node->key32){
node=node->right;
continue;
}
}
return NULL;
}
void
trbt_delete32(trbt_tree_t *tree, uint32_t key)
{
trbt_node_t *node;
node=tree->tree;
while(node){
if(key==node->key32){
delete_node(node);
return;
}
if(keykey32){
node=node->left;
continue;
}
if(key>node->key32){
node=node->right;
continue;
}
}
}
# if 0
static void printtree(trbt_node_t *node, int levels)
{
int i;
if(node==NULL)return;
printtree(node->left, levels+1);
for(i=0;ikey32,node->rb_color==TRBT_BLACK?"BLACK":"RED");
printtree(node->right, levels+1);
printf("\n");
}
void print_tree(trbt_tree_t *tree)
{
if(tree->tree==NULL){
printf("tree is empty\n");
return;
}
printf("---\n");
printtree(tree->tree->left, 1);
printf("root node key:%d COLOR:%s\n",tree->tree->key32,tree->tree->rb_color==TRBT_BLACK?"BLACK":"RED");
printtree(tree->tree->right, 1);
printf("===\n");
}
void
test_tree(void)
{
trbt_tree_t *tree;
char *str;
int i, ret;
int NUM=15;
int cnt=0;
tree=trbt_create(talloc_new(NULL));
#if 0
for(i=0;i<10;i++){
printf("adding node %i\n",i);
trbt_insert32(tree, i, NULL);
print_tree(tree);
}
printf("deleting node %i\n",3);
trbt_delete32(tree, 3);
print_tree(tree);
for(i=0;i<10;i++){
printf("deleting node %i\n",i);
trbt_delete32(tree, i);
print_tree(tree);
}
exit(0);
#endif
while(++cnt){
int i;
printf("iteration : %d\n",cnt);
i=random()%20;
printf("adding node %i\n",i);
trbt_insert32(tree, i, NULL);
print_tree(tree);
i=random()%20;
printf("deleting node %i\n",i);
trbt_delete32(tree, i);
print_tree(tree);
}
}
#endif