Files
bind9/lib/isc/rbtgen.c
1999-03-04 02:37:49 +00:00

531 lines
13 KiB
C

/*
* Copyright (C) 1998, 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* This file is a generic template that can be used to create a red-black
* tree library for a specified node type.
*/
/*
* Red-Black Tree algorithms adapted from:
*
* _Introduction to Algorithms_, Cormen, Leiserson, and Rivest,
* MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 14.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <isc/rbtgen.h>
#include <isc/boolean.h>
#include <isc/assertions.h>
#ifdef RBT_TRACE
#define TRACE_CASE(n) printf("case %d\n", (n))
#define TRACE_NODE(n) \
do { \
printf("%p ", (n)); \
PRINT_KEY(KEY(n)); \
printf(" (%s)\n", IS_RED((n)) ? "red" : "black"); \
} while (0)
#else
#define TRACE_CASE(n)
#define TRACE_NODE(n)
#endif
#define COLOR(node) ((node)->color)
#define KEY(node) ((node)->data)
#define LEFT(node) ((node)->left)
#define RIGHT(node) ((node)->right)
#define PARENT(node) ((node)->parent)
#define SET_COLOR(node, value) ((node)->color = (value))
#define SET_KEY(node, value) ((node)->data = (value))
#define SET_LEFT(node, child) ((node)->left = (child))
#define SET_RIGHT(node, child) ((node)->right = (child))
#define SET_PARENT(node, child) ((node)->parent = (child))
#define IS_RED(node) ((node) != NULL && (node)->color == red)
#define IS_BLACK(node) ((node) == NULL || (node)->color == black)
#define MAKE_RED(node) ((node)->color = red)
#define MAKE_BLACK(node) ((node)->color = black)
static inline void
rotate_left(RBT_NODE *node, RBT_NODE **rootp) {
RBT_NODE *child;
REQUIRE(node != NULL);
REQUIRE(rootp != NULL);
child = RIGHT(node);
REQUIRE(child != NULL);
SET_RIGHT(node, LEFT(child));
if (LEFT(child) != NULL)
SET_PARENT(LEFT(child), node);
SET_LEFT(child, node);
SET_PARENT(child, PARENT(node));
if (PARENT(node) != NULL) {
if (LEFT(PARENT(node)) == node)
SET_LEFT(PARENT(node), child);
else {
SET_RIGHT(PARENT(node), child);
}
} else
*rootp = child;
SET_PARENT(node, child);
}
static inline void
rotate_right(RBT_NODE *node, RBT_NODE **rootp) {
RBT_NODE *child;
REQUIRE(node != NULL);
REQUIRE(rootp != NULL);
child = LEFT(node);
REQUIRE(child != NULL);
SET_LEFT(node, RIGHT(child));
if (RIGHT(child) != NULL)
SET_PARENT(RIGHT(child), node);
SET_RIGHT(child, node);
SET_PARENT(child, PARENT(node));
if (PARENT(node) != NULL) {
if (LEFT(PARENT(node)) == node)
SET_LEFT(PARENT(node), child);
else
SET_RIGHT(PARENT(node), child);
} else
*rootp = child;
SET_PARENT(node, child);
}
isc_result_t
RBT_INSERT(RBT_NODE *node, RBT_NODE **rootp, int (*compare)(void *, void *)) {
RBT_NODE *current, *child, *root, *parent, *grandparent;
int i;
REQUIRE(rootp != NULL);
REQUIRE(LEFT(node) == NULL && RIGHT(node) == NULL);
REQUIRE(KEY(node) != NULL);
root = *rootp;
if (root == NULL) {
MAKE_BLACK(node);
*rootp = node;
return (ISC_R_SUCCESS);
}
current = NULL;
child = root;
do {
current = child;
i = compare(KEY(node), KEY(current));
if (i == 0)
return (ISC_R_EXISTS);
if (i < 0)
child = LEFT(current);
else
child = RIGHT(current);
} while (child != NULL);
if (i < 0)
SET_LEFT(current, node);
else
SET_RIGHT(current, node);
MAKE_RED(node);
SET_PARENT(node, current);
while (node != root && IS_RED(PARENT(node))) {
parent = PARENT(node);
grandparent = PARENT(PARENT(node));
TRACE_NODE(n);
if (parent == LEFT(grandparent)) {
child = RIGHT(grandparent);
if (child != NULL && IS_RED(child)) {
TRACE_CASE(1);
MAKE_BLACK(parent);
MAKE_BLACK(child);
MAKE_RED(grandparent);
node = grandparent;
} else {
if (node == RIGHT(parent)) {
TRACE_CASE(2);
node = parent;
rotate_left(node, &root);
parent = PARENT(node);
grandparent = PARENT(PARENT(node));
}
TRACE_CASE(3);
MAKE_BLACK(parent);
MAKE_RED(grandparent);
rotate_right(grandparent, &root);
}
} else {
child = LEFT(grandparent);
if (child != NULL && IS_RED(child)) {
TRACE_CASE(4);
MAKE_BLACK(parent);
MAKE_BLACK(child);
MAKE_RED(grandparent);
node = grandparent;
} else {
if (node == LEFT(parent)) {
TRACE_CASE(5);
node = parent;
rotate_right(node, &root);
parent = PARENT(node);
grandparent = PARENT(PARENT(node));
}
TRACE_CASE(6);
MAKE_BLACK(parent);
MAKE_RED(grandparent);
rotate_left(grandparent, &root);
}
}
}
MAKE_BLACK(root);
*rootp = root;
return (ISC_R_SUCCESS);
}
/* node must belong to a pointer in the tree; how could this be ensured? */
isc_result_t
RBT_DELETE(RBT_NODE *delete, RBT_NODE **rootp) {
RBT_NODE *successor, *sibling, *child = NULL;
REQUIRE(rootp != NULL);
REQUIRE(delete);
if (LEFT(delete) == NULL)
if (RIGHT(delete) == NULL) {
if (*rootp == delete) {
/* this is the only item in the tree */
*rootp = NULL;
return(ISC_R_SUCCESS);
}
} else
/* this node has one child, on the right */
child = RIGHT(delete);
else if (RIGHT(delete) == NULL)
/* this node has one child, on the left */
child = LEFT(delete);
else {
RBT_NODE holder, *tmp = &holder;
/* this node has two children, so it cannot be directly
deleted. find its immediate in-order successor and
move it to this location, then do the deletion at the
old site of the successor */
successor = RIGHT(delete);
while (LEFT(successor) != NULL)
successor = LEFT(successor);
/* the successor cannot possibly have a left child;
if there is any child, it is on the right */
if (RIGHT(successor))
child = RIGHT(successor);
/* swap the two nodes; it would be simpler to just replace
the value being deleted with that of the successor,
but this rigamarole is done so the caller has complete
control over the pointers (and memory allocation) of
all of nodes. if just the key value were removed from
the tree, the pointer to the node would would be
unchanged. */
/* first, put the successor in the tree location of the
node to be deleted */
memcpy(tmp, successor, sizeof(RBT_NODE));
if (LEFT(PARENT(delete)) == delete)
SET_LEFT(PARENT(delete), successor);
else
SET_RIGHT(PARENT(delete), successor);
SET_PARENT(successor, PARENT(delete));
if (LEFT(delete) != NULL)
SET_PARENT(LEFT(delete), successor);
if (RIGHT(delete) != NULL && RIGHT(delete) != successor)
SET_PARENT(RIGHT(delete), successor);
SET_COLOR(successor, COLOR(delete));
SET_LEFT(successor, LEFT(delete));
SET_RIGHT(successor, RIGHT(delete));
/* now relink the node to be deleted into the
successor's previous tree location */
if (PARENT(tmp) != delete) {
if (LEFT(PARENT(tmp)) == successor)
SET_LEFT(PARENT(tmp), delete);
else
SET_RIGHT(PARENT(tmp), delete);
SET_PARENT(delete, PARENT(tmp));
} else
SET_PARENT(delete, successor);
/* original successor node has no left */
if (RIGHT(tmp) != NULL)
SET_PARENT(RIGHT(tmp), delete);
SET_COLOR(delete, COLOR(tmp));
SET_LEFT(delete, LEFT(tmp));
SET_RIGHT(delete, RIGHT(tmp));
}
/* fix the parent chain if a non-leaf is being deleted */
if (PARENT(delete) != NULL) {
if (LEFT(PARENT(delete)) == delete) {
SET_LEFT(PARENT(delete), child);
sibling = RIGHT(PARENT(delete));
} else {
SET_RIGHT(PARENT(delete), child);
sibling = LEFT(PARENT(delete));
}
if (child != NULL)
SET_PARENT(child, PARENT(delete));
} else {
/* this is the root being deleted, with just one child */
SET_PARENT(child, NULL);
sibling= NULL;
*rootp = child;
}
/* fix color violations */
if (IS_BLACK(delete)) {
RBT_NODE *parent;
parent = PARENT(delete);
while (child != *rootp && IS_BLACK(child)) {
/* parent = PARENT(parent_pointer); */
if (LEFT(parent) == child) {
sibling = RIGHT(parent);
if (IS_RED(sibling)) {
MAKE_BLACK(sibling);
MAKE_RED(parent);
rotate_left(parent, rootp);
sibling = RIGHT(parent);
}
if (IS_BLACK(LEFT(sibling)) &&
IS_BLACK(RIGHT(sibling))) {
MAKE_RED(sibling);
child = parent;
} else {
if (IS_BLACK(RIGHT(sibling))) {
MAKE_BLACK(LEFT(sibling));
MAKE_RED(sibling);
rotate_right(sibling, rootp);
sibling = RIGHT(parent);
}
SET_COLOR(sibling, COLOR(parent));
MAKE_BLACK(parent);
MAKE_BLACK(RIGHT(sibling));
rotate_left(parent, rootp);
child = *rootp;
}
} else {
sibling = LEFT(parent);
if (IS_RED(sibling)) {
MAKE_BLACK(sibling);
MAKE_RED(parent);
rotate_right(parent, rootp);
sibling = LEFT(parent);
}
if (IS_BLACK(LEFT(sibling)) &&
IS_BLACK(RIGHT(sibling))) {
MAKE_RED(sibling);
child = parent;
} else {
if (IS_BLACK(LEFT(sibling))) {
MAKE_BLACK(RIGHT(sibling));
MAKE_RED(sibling);
rotate_left(sibling, rootp);
sibling = LEFT(parent);
}
SET_COLOR(sibling, COLOR(parent));
MAKE_BLACK(parent);
MAKE_BLACK(LEFT(sibling));
rotate_right(parent, rootp);
child = *rootp;
}
}
parent = PARENT(child);
}
if (IS_RED(child))
MAKE_BLACK(child);
}
return(ISC_R_SUCCESS);
}
RBT_NODE *
RBT_SEARCH(RBT_NODE *current, void *key, int (*compare)(void *, void *)) {
int i;
while (current != NULL) {
i = compare(key, KEY(current));
if (i == 0)
break;
if (i < 0)
current = LEFT(current);
else
current = RIGHT(current);
}
return(current);
}
static inline void
print_tree(RBT_NODE *root, void (*print_key)(void *), int depth) {
int i;
for (i = 0; i < depth; i++)
putchar('\t');
if (root != NULL) {
print_key(KEY(root));
printf(" (%s", IS_RED(root) ? "RED" : "black");
if (root->parent) {
printf(" from ");
print_key(KEY(root->parent));
}
printf(")\n");
depth++;
if (IS_RED(root) && IS_RED(LEFT(root)))
printf("** Red/Red color violation on left\n");
print_tree(LEFT(root), print_key, depth);
if (IS_RED(root) && IS_RED(RIGHT(root)))
printf("** Red/Red color violation on right\n");
print_tree(RIGHT(root), print_key, depth);
} else
printf("NULL\n");
}
void
RBT_PRINT(RBT_NODE *root, void (*print_key)(void *)) {
print_tree(root, print_key, 0);
}
#ifdef WANT_RBTGEN_MAIN
int compare_int(void *, void *);
void print_int(void *);
int ints[] = { 12679, 26804, 7389, 20562, 24355, 23584,
10713, 8094, 19071, 6732, 2709, 6058,
3995, 3896, 15121, 1142, 2679, 26340,
30541, 29186,
#if 0
6584, 13201, 21238, 30455, 6500, 30669,
10370, 2963, 26576, 17353, 19150, 19951,
12540, 21381, 17882, 23051, 24808, 8961,
26022, 3047, 9108, 28221, 13874, 32643,
25856, 12601, 894, 4319, 20780, 10229,
#endif
0 };
int
compare_int(void *a, void *b) {
int i = *(int *)a, j = *(int *)b;
return(i == j ? 0 : (i < j ? -1 : 1));
}
void
print_int(void *num) {
int i = *(int *)num;
(void)printf("%d", i);
}
#define KEY_VALUE(node, type) (*(type *)((node)->data))
void
main() {
RBT_NODE nodes[sizeof(ints)/sizeof(int)], *p, *root;
int *i, j;
setbuf(stdout, NULL);
printf("For any two successive numbers at the same depth, the\n");
printf("first number (and all its descendants) should be less than\n");
printf("the number which immediately precedes it one depth level\n");
printf("higher, and the second number (and all its descendants)\n");
printf("should be greater than that preceding number\n\n");
for (i = &ints[0], p = &nodes[0]; *i != 0; i++, p++) {
printf("inserting %d\n", *i);
KEY(p) = i;
RBT_INSERT(p, &root, compare_int);
RBT_PRINT(root, print_int);
}
j = 2679;
i = &j;
printf("searching for %d ...", j);
p = RBT_SEARCH(root, i, compare_int);
if (p != NULL) {
printf("found %d\n", KEY_VALUE(p, int));
} else {
printf("not found!\n");
}
j = 9999;
i = &j;
printf("searching for %d ...", j);
p = RBT_SEARCH(root, i, compare_int);
if (p != NULL) {
printf("found %d\n", KEY_VALUE(p, int));
} else {
printf("not found!\n");
}
p = &nodes[sizeof(ints) / sizeof(int) - 2];
do {
printf("deleting %d\n", KEY_VALUE(p, int));
RBT_DELETE(p, &root);
RBT_PRINT(root, print_int);
} while (p-- != &nodes[0]);
exit(0);
}
#endif