1
0
mirror of https://gitlab.xiph.org/xiph/icecast-common.git synced 2024-06-09 06:10:42 +00:00
icecast-common/avl/avl.c
2018-10-12 11:27:17 +00:00

1196 lines
28 KiB
C

/*
* Copyright (C) 1995-1997 Sam Rushing <rushing@nightmare.com>
* Copyright (C) 2012-2018 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of Sam
* Rushing not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission.
*
* SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, 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.
*
*/
/* $Id: avl.c,v 1.11 2004/01/27 02:16:25 karl Exp $ */
/*
* This is a fairly straightfoward translation of a prototype
* written in python, 'igloo_avl_tree.py'. Read that file first.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "avl.h"
igloo_avl_node *
igloo_avl_node_new (void * key,
igloo_avl_node * parent)
{
igloo_avl_node * node = (igloo_avl_node *) malloc (sizeof (igloo_avl_node));
if (!node) {
return NULL;
} else {
node->parent = parent;
node->key = key;
node->left = NULL;
node->right = NULL;
node->rank_and_balance = 0;
AVL_SET_BALANCE (node, 0);
AVL_SET_RANK (node, 1);
#ifdef HAVE_AVL_NODE_LOCK
thread_rwlock_create(&node->rwlock);
#endif
return node;
}
}
igloo_avl_tree *
igloo_avl_tree_new (igloo_avl_key_compare_fun_type compare_fun,
void * compare_arg)
{
igloo_avl_tree * t = (igloo_avl_tree *) malloc (sizeof (igloo_avl_tree));
if (!t) {
return NULL;
} else {
igloo_avl_node * root = igloo_avl_node_new((void *)NULL, (igloo_avl_node *) NULL);
if (!root) {
free (t);
return NULL;
} else {
t->root = root;
t->height = 0;
t->length = 0;
t->compare_fun = compare_fun;
t->compare_arg = compare_arg;
thread_rwlock_create(&t->rwlock);
return t;
}
}
}
static void
igloo_avl_tree_free_helper (igloo_avl_node * node, igloo_avl_free_key_fun_type free_key_fun)
{
if (node->left) {
igloo_avl_tree_free_helper (node->left, free_key_fun);
}
if (free_key_fun)
free_key_fun (node->key);
if (node->right) {
igloo_avl_tree_free_helper (node->right, free_key_fun);
}
#ifdef HAVE_AVL_NODE_LOCK
igloo_thread_rwlock_destroy (&node->rwlock);
#endif
free (node);
}
void
igloo_avl_tree_free (igloo_avl_tree * tree, igloo_avl_free_key_fun_type free_key_fun)
{
if (tree->length) {
igloo_avl_tree_free_helper (tree->root->right, free_key_fun);
}
if (tree->root) {
#ifdef HAVE_AVL_NODE_LOCK
igloo_thread_rwlock_destroy(&tree->root->rwlock);
#endif
free (tree->root);
}
igloo_thread_rwlock_destroy(&tree->rwlock);
free (tree);
}
int
igloo_avl_insert (igloo_avl_tree * ob,
void * key)
{
if (!(ob->root->right)) {
igloo_avl_node * node = igloo_avl_node_new (key, ob->root);
if (!node) {
return -1;
} else {
ob->root->right = node;
ob->length = ob->length + 1;
return 0;
}
} else { /* not self.right == None */
igloo_avl_node *t, *p, *s, *q, *r;
int a;
t = ob->root;
s = p = t->right;
while (1) {
if (ob->compare_fun (ob->compare_arg, key, p->key) < 1) {
/* move left */
AVL_SET_RANK (p, (AVL_GET_RANK (p) + 1));
q = p->left;
if (!q) {
/* insert */
igloo_avl_node * q_node = igloo_avl_node_new (key, p);
if (!q_node) {
return (-1);
} else {
q = q_node;
p->left = q;
break;
}
} else if (AVL_GET_BALANCE(q)) {
t = p;
s = q;
}
p = q;
} else {
/* move right */
q = p->right;
if (!q) {
/* insert */
igloo_avl_node * q_node = igloo_avl_node_new (key, p);
if (!q_node) {
return -1;
} else {
q = q_node;
p->right = q;
break;
}
} else if (AVL_GET_BALANCE(q)) {
t = p;
s = q;
}
p = q;
}
}
ob->length = ob->length + 1;
/* adjust balance factors */
if (ob->compare_fun (ob->compare_arg, key, s->key) < 1) {
r = p = s->left;
} else {
r = p = s->right;
}
while (p != q) {
if (ob->compare_fun (ob->compare_arg, key, p->key) < 1) {
AVL_SET_BALANCE (p, -1);
p = p->left;
} else {
AVL_SET_BALANCE (p, +1);
p = p->right;
}
}
/* balancing act */
if (ob->compare_fun (ob->compare_arg, key, s->key) < 1) {
a = -1;
} else {
a = +1;
}
if (AVL_GET_BALANCE (s) == 0) {
AVL_SET_BALANCE (s, a);
ob->height = ob->height + 1;
return 0;
} else if (AVL_GET_BALANCE (s) == -a) {
AVL_SET_BALANCE (s, 0);
return 0;
} else if (AVL_GET_BALANCE(s) == a) {
if (AVL_GET_BALANCE (r) == a) {
/* single rotation */
p = r;
if (a == -1) {
s->left = r->right;
if (r->right) {
r->right->parent = s;
}
r->right = s;
s->parent = r;
AVL_SET_RANK (s, (AVL_GET_RANK (s) - AVL_GET_RANK (r)));
} else {
s->right = r->left;
if (r->left) {
r->left->parent = s;
}
r->left = s;
s->parent = r;
AVL_SET_RANK (r, (AVL_GET_RANK (r) + AVL_GET_RANK (s)));
}
AVL_SET_BALANCE (s, 0);
AVL_SET_BALANCE (r, 0);
} else if (AVL_GET_BALANCE (r) == -a) {
/* double rotation */
if (a == -1) {
p = r->right;
r->right = p->left;
if (p->left) {
p->left->parent = r;
}
p->left = r;
r->parent = p;
s->left = p->right;
if (p->right) {
p->right->parent = s;
}
p->right = s;
s->parent = p;
AVL_SET_RANK (p, (AVL_GET_RANK (p) + AVL_GET_RANK (r)));
AVL_SET_RANK (s, (AVL_GET_RANK (s) - AVL_GET_RANK (p)));
} else {
p = r->left;
r->left = p->right;
if (p->right) {
p->right->parent = r;
}
p->right = r;
r->parent = p;
s->right = p->left;
if (p->left) {
p->left->parent = s;
}
p->left = s;
s->parent = p;
AVL_SET_RANK (r, (AVL_GET_RANK (r) - AVL_GET_RANK (p)));
AVL_SET_RANK (p, (AVL_GET_RANK (p) + AVL_GET_RANK (s)));
}
if (AVL_GET_BALANCE (p) == a) {
AVL_SET_BALANCE (s, -a);
AVL_SET_BALANCE (r, 0);
} else if (AVL_GET_BALANCE (p) == -a) {
AVL_SET_BALANCE (s, 0);
AVL_SET_BALANCE (r, a);
} else {
AVL_SET_BALANCE (s, 0);
AVL_SET_BALANCE (r, 0);
}
AVL_SET_BALANCE (p, 0);
}
/* finishing touch */
if (s == t->right) {
t->right = p;
} else {
t->left = p;
}
p->parent = t;
}
}
return 0;
}
int
igloo_avl_get_by_index (igloo_avl_tree * tree,
unsigned long index,
void ** value_address)
{
igloo_avl_node * p = tree->root->right;
unsigned long m = index + 1;
while (1) {
if (!p) {
return -1;
}
if (m < AVL_GET_RANK(p)) {
p = p->left;
} else if (m > AVL_GET_RANK(p)) {
m = m - AVL_GET_RANK(p);
p = p->right;
} else {
*value_address = p->key;
return 0;
}
}
}
int
igloo_avl_get_by_key (igloo_avl_tree * tree,
void * key,
void **value_address)
{
igloo_avl_node * x = tree->root->right;
if (!x) {
return -1;
}
while (1) {
int compare_result = tree->compare_fun (tree->compare_arg, key, x->key);
if (compare_result < 0) {
if (x->left) {
x = x->left;
} else {
return -1;
}
} else if (compare_result > 0) {
if (x->right) {
x = x->right;
} else {
return -1;
}
} else {
*value_address = x->key;
return 0;
}
}
}
int igloo_avl_delete(igloo_avl_tree *tree, void *key, igloo_avl_free_key_fun_type free_key_fun)
{
igloo_avl_node *x, *y, *p, *q, *r, *top, *x_child;
int shortened_side, shorter;
x = tree->root->right;
if (!x) {
return -1;
}
while (1) {
int compare_result = tree->compare_fun (tree->compare_arg, key, x->key);
if (compare_result < 0) {
/* move left
* We will be deleting from the left, adjust this node's
* rank accordingly
*/
AVL_SET_RANK (x, (AVL_GET_RANK(x) - 1));
if (x->left) {
x = x->left;
} else {
/* Oops! now we have to undo the rank changes
* all the way up the tree
*/
AVL_SET_RANK(x, (AVL_GET_RANK (x) + 1));
while (x != tree->root->right) {
if (x->parent->left == x) {
AVL_SET_RANK(x->parent, (AVL_GET_RANK (x->parent) + 1));
}
x = x->parent;
}
return -1; /* key not in tree */
}
} else if (compare_result > 0) {
/* move right */
if (x->right) {
x = x->right;
} else {
AVL_SET_RANK(x, (AVL_GET_RANK (x) + 1));
while (x != tree->root->right) {
if (x->parent->left == x) {
AVL_SET_RANK(x->parent, (AVL_GET_RANK (x->parent) + 1));
}
x = x->parent;
}
return -1; /* key not in tree */
}
} else {
break;
}
}
if (x->left && x->right) {
void * temp_key;
/* The complicated case.
* reduce this to the simple case where we are deleting
* a node with at most one child.
*/
/* find the immediate predecessor <y> */
y = x->left;
while (y->right) {
y = y->right;
}
/* swap <x> with <y> */
temp_key = x->key;
x->key = y->key;
y->key = temp_key;
/* we know <x>'s left subtree lost a node because that's
* where we took it from
*/
AVL_SET_RANK (x, (AVL_GET_RANK (x) - 1));
x = y;
}
/* now <x> has at most one child
* scoot this child into the place of <x>
*/
if (x->left) {
x_child = x->left;
x_child->parent = x->parent;
} else if (x->right) {
x_child = x->right;
x_child->parent = x->parent;
} else {
x_child = NULL;
}
/* now tell <x>'s parent that a grandchild became a child */
if (x == x->parent->left) {
x->parent->left = x_child;
shortened_side = -1;
} else {
x->parent->right = x_child;
shortened_side = +1;
}
/*
* the height of the subtree <x>
* has now been shortened. climb back up
* the tree, rotating when necessary to adjust
* for the change.
*/
shorter = 1;
p = x->parent;
/* return the key and node to storage */
if (free_key_fun)
free_key_fun (x->key);
#ifdef HAVE_AVL_NODE_LOCK
igloo_thread_rwlock_destroy (&x->rwlock);
#endif
free (x);
while (shorter && p->parent) {
/* case 1: height unchanged */
if (AVL_GET_BALANCE(p) == 0) {
if (shortened_side == -1) {
/* we removed a left child, the tree is now heavier
* on the right
*/
AVL_SET_BALANCE (p, +1);
} else {
/* we removed a right child, the tree is now heavier
* on the left
*/
AVL_SET_BALANCE (p, -1);
}
shorter = 0;
} else if (AVL_GET_BALANCE (p) == shortened_side) {
/* case 2: taller subtree shortened, height reduced */
AVL_SET_BALANCE (p, 0);
} else {
/* case 3: shorter subtree shortened */
top = p->parent;
/* set <q> to the taller of the two subtrees of <p> */
if (shortened_side == 1) {
q = p->left;
} else {
q = p->right;
}
if (AVL_GET_BALANCE (q) == 0) {
/* case 3a: height unchanged */
if (shortened_side == -1) {
/* single rotate left */
q->parent = p->parent;
p->right = q->left;
if (q->left) {
q->left->parent = p;
}
q->left = p;
p->parent = q;
AVL_SET_RANK (q, (AVL_GET_RANK (q) + AVL_GET_RANK (p)));
} else {
/* single rotate right */
q->parent = p->parent;
p->left = q->right;
if (q->right) {
q->right->parent = p;
}
q->right = p;
p->parent = q;
AVL_SET_RANK (p, (AVL_GET_RANK (p) - AVL_GET_RANK (q)));
}
shorter = 0;
AVL_SET_BALANCE (q, shortened_side);
AVL_SET_BALANCE (p, (- shortened_side));
} else if (AVL_GET_BALANCE (q) == AVL_GET_BALANCE (p)) {
/* case 3b: height reduced */
if (shortened_side == -1) {
/* single rotate left */
q->parent = p->parent;
p->right = q->left;
if (q->left) {
q->left->parent = p;
}
q->left = p;
p->parent = q;
AVL_SET_RANK (q, (AVL_GET_RANK (q) + AVL_GET_RANK (p)));
} else {
/* single rotate right */
q->parent = p->parent;
p->left = q->right;
if (q->right) {
q->right->parent = p;
}
q->right = p;
p->parent = q;
AVL_SET_RANK (p, (AVL_GET_RANK (p) - AVL_GET_RANK (q)));
}
shorter = 1;
AVL_SET_BALANCE (q, 0);
AVL_SET_BALANCE (p, 0);
} else {
/* case 3c: height reduced, balance factors opposite */
if (shortened_side == 1) {
/* double rotate right */
/* first, a left rotation around q */
r = q->right;
r->parent = p->parent;
q->right = r->left;
if (r->left) {
r->left->parent = q;
}
r->left = q;
q->parent = r;
/* now, a right rotation around p */
p->left = r->right;
if (r->right) {
r->right->parent = p;
}
r->right = p;
p->parent = r;
AVL_SET_RANK (r, (AVL_GET_RANK (r) + AVL_GET_RANK (q)));
AVL_SET_RANK (p, (AVL_GET_RANK (p) - AVL_GET_RANK (r)));
} else {
/* double rotate left */
/* first, a right rotation around q */
r = q->left;
r->parent = p->parent;
q->left = r->right;
if (r->right) {
r->right->parent = q;
}
r->right = q;
q->parent = r;
/* now a left rotation around p */
p->right = r->left;
if (r->left) {
r->left->parent = p;
}
r->left = p;
p->parent = r;
AVL_SET_RANK (q, (AVL_GET_RANK (q) - AVL_GET_RANK (r)));
AVL_SET_RANK (r, (AVL_GET_RANK (r) + AVL_GET_RANK (p)));
}
if (AVL_GET_BALANCE (r) == shortened_side) {
AVL_SET_BALANCE (q, (- shortened_side));
AVL_SET_BALANCE (p, 0);
} else if (AVL_GET_BALANCE (r) == (- shortened_side)) {
AVL_SET_BALANCE (q, 0);
AVL_SET_BALANCE (p, shortened_side);
} else {
AVL_SET_BALANCE (q, 0);
AVL_SET_BALANCE (p, 0);
}
AVL_SET_BALANCE (r, 0);
q = r;
}
/* a rotation has caused <q> (or <r> in case 3c) to become
* the root. let <p>'s former parent know this.
*/
if (top->left == p) {
top->left = q;
} else {
top->right = q;
}
/* end case 3 */
p = q;
}
x = p;
p = x->parent;
/* shortened_side tells us which side we came up from */
if (x == p->left) {
shortened_side = -1;
} else {
shortened_side = +1;
}
} /* end while(shorter) */
/* when we're all done, we're one shorter */
tree->length = tree->length - 1;
return (0);
}
static int
igloo_avl_iterate_inorder_helper (igloo_avl_node * node,
igloo_avl_iter_fun_type iter_fun,
void * iter_arg)
{
int result;
if (node->left) {
result = igloo_avl_iterate_inorder_helper (node->left, iter_fun, iter_arg);
if (result != 0) {
return result;
}
}
result = (iter_fun (node->key, iter_arg));
if (result != 0) {
return result;
}
if (node->right) {
result = igloo_avl_iterate_inorder_helper (node->right, iter_fun, iter_arg);
if (result != 0) {
return result;
}
}
return 0;
}
int
igloo_avl_iterate_inorder (igloo_avl_tree * tree,
igloo_avl_iter_fun_type iter_fun,
void * iter_arg)
{
int result;
if (tree->length) {
result = igloo_avl_iterate_inorder_helper (tree->root->right, iter_fun, iter_arg);
return (result);
} else {
return 0;
}
}
igloo_avl_node *igloo_avl_get_first(igloo_avl_tree *tree)
{
igloo_avl_node *node;
node = tree->root->right;
if (node == NULL || node->key == NULL) return NULL;
while (node->left)
node = node->left;
return node;
}
igloo_avl_node *igloo_avl_get_prev(igloo_avl_node *node)
{
if (node->left) {
node = node->left;
while (node->right) {
node = node->right;
}
return node;
} else {
igloo_avl_node *child = node;
while (node->parent && node->parent->key) {
node = node->parent;
if (child == node->right) {
return node;
}
child = node;
}
return NULL;
}
}
igloo_avl_node *igloo_avl_get_next(igloo_avl_node *node)
{
if (node->right) {
node = node->right;
while (node->left) {
node = node->left;
}
return node;
} else {
igloo_avl_node *child = node;
while (node->parent && node->parent->key) {
node = node->parent;
if (child == node->left) {
return node;
}
child = node;
}
return NULL;
}
}
/* iterate a function over a range of indices, using get_predecessor */
int
igloo_avl_iterate_index_range (igloo_avl_tree * tree,
igloo_avl_iter_index_fun_type iter_fun,
unsigned long low,
unsigned long high,
void * iter_arg)
{
unsigned long m;
unsigned long num_left;
igloo_avl_node * node;
if (high > tree->length) {
return -1;
}
num_left = (high - low);
/* find the <high-1>th node */
m = high;
node = tree->root->right;
while (1) {
if (m < AVL_GET_RANK (node)) {
node = node->left;
} else if (m > AVL_GET_RANK (node)) {
m = m - AVL_GET_RANK (node);
node = node->right;
} else {
break;
}
}
/* call <iter_fun> on <node>, <get_pred(node)>, ... */
while (num_left) {
num_left = num_left - 1;
if (iter_fun (num_left, node->key, iter_arg) != 0) {
return -1;
}
node = igloo_avl_get_prev (node);
}
return 0;
}
/* If <key> is present in the tree, return that key's node, and set <*index>
* appropriately. If not, return NULL, and set <*index> to the position
* representing the closest preceding value.
*/
static igloo_avl_node *
igloo_avl_get_index_by_key (igloo_avl_tree * tree,
void * key,
unsigned long * index)
{
igloo_avl_node * x = tree->root->right;
unsigned long m;
if (!x) {
return NULL;
}
m = AVL_GET_RANK (x);
while (1) {
int compare_result = tree->compare_fun (tree->compare_arg, key, x->key);
if (compare_result < 0) {
if (x->left) {
m = m - AVL_GET_RANK(x);
x = x->left;
m = m + AVL_GET_RANK(x);
} else {
*index = m - 2;
return NULL;
}
} else if (compare_result > 0) {
if (x->right) {
x = x->right;
m = m + AVL_GET_RANK(x);
} else {
*index = m - 1;
return NULL;
}
} else {
*index = m - 1;
return x;
}
}
}
/* return the (low index, high index) pair that spans the given key */
int
igloo_avl_get_span_by_key (igloo_avl_tree * tree,
void * key,
unsigned long * low,
unsigned long * high)
{
unsigned long m, i, j;
igloo_avl_node * node;
node = igloo_avl_get_index_by_key (tree, key, &m);
/* did we find an exact match?
* if so, we have to search left and right
* to find the span, since we know nothing about
* the arrangement of like keys.
*/
if (node) {
igloo_avl_node * left, * right;
/* search left */
left = igloo_avl_get_prev (node);
i = m;
while (left && (i > 0) && (tree->compare_fun (tree->compare_arg, key, left->key) == 0)) {
left = igloo_avl_get_prev (left);
i = i - 1;
}
/* search right */
right = igloo_avl_get_next (node);
j = m;
while (right && (j <= tree->length) && (tree->compare_fun (tree->compare_arg, key, right->key) == 0)) {
right = igloo_avl_get_next (right);
j = j + 1;
}
*low = i;
*high = j + 1;
return 0;
} else {
*low = *high = m;
}
return 0;
}
/* return the (low index, high index) pair that spans the given key */
int
igloo_avl_get_span_by_two_keys (igloo_avl_tree * tree,
void * low_key,
void * high_key,
unsigned long * low,
unsigned long * high)
{
unsigned long i, j;
igloo_avl_node * low_node, * high_node;
int order;
/* we may need to swap them */
order = tree->compare_fun (tree->compare_arg, low_key, high_key);
if (order > 0) {
void * temp = low_key;
low_key = high_key;
high_key = temp;
}
low_node = igloo_avl_get_index_by_key (tree, low_key, &i);
high_node = igloo_avl_get_index_by_key (tree, high_key, &j);
if (low_node) {
igloo_avl_node * left;
/* search left */
left = igloo_avl_get_prev (low_node);
while (left && (i > 0) && (tree->compare_fun (tree->compare_arg, low_key, left->key) == 0)) {
left = igloo_avl_get_prev (left);
i = i - 1;
}
} else {
i = i + 1;
}
if (high_node) {
igloo_avl_node * right;
/* search right */
right = igloo_avl_get_next (high_node);
while (right && (j <= tree->length) && (tree->compare_fun (tree->compare_arg, high_key, right->key) == 0)) {
right = igloo_avl_get_next (right);
j = j + 1;
}
} else {
j = j + 1;
}
*low = i;
*high = j;
return 0;
}
int
igloo_avl_get_item_by_key_most (igloo_avl_tree * tree,
void * key,
void **value_address)
{
igloo_avl_node * x = tree->root->right;
*value_address = NULL;
if (!x) {
return -1;
}
while (1) {
int compare_result = tree->compare_fun (tree->compare_arg, key, x->key);
if (compare_result == 0) {
*value_address = x->key;
return 0;
} else if (compare_result < 0) {
/* the given key is less than the current key */
if (x->left) {
x = x->left;
} else {
if (*value_address)
return 0;
else
return -1;
}
} else {
/* the given key is more than the current key */
/* save this value, it might end up being the right one! */
*value_address = x->key;
if (x->right) {
/* there is a bigger entry */
x = x->right;
} else {
if (*value_address)
return 0;
else
return -1;
}
}
}
}
int
igloo_avl_get_item_by_key_least (igloo_avl_tree * tree,
void * key,
void **value_address)
{
igloo_avl_node * x = tree->root->right;
*value_address = NULL;
if (!x) {
return -1;
}
while (1) {
int compare_result = tree->compare_fun (tree->compare_arg, key, x->key);
if (compare_result == 0) {
*value_address = x->key;
return 0; /* exact match */
} else if (compare_result < 0) {
/* the given key is less than the current key */
/* save this value, it might end up being the right one! */
*value_address = x->key;
if (x->left) {
x = x->left;
} else {
if (*value_address) /* we have found a valid entry */
return 0;
else
return -1;
}
} else {
if (x->right) {
/* there is a bigger entry */
x = x->right;
} else {
if (*value_address) /* we have found a valid entry */
return 0;
else
return -1;
}
}
}
}
#define AVL_MAX(X, Y) ((X) > (Y) ? (X) : (Y))
static long
igloo_avl_verify_balance (igloo_avl_node * node)
{
if (!node) {
return 0;
} else {
long lh = igloo_avl_verify_balance (node->left);
long rh = igloo_avl_verify_balance (node->right);
if ((rh - lh) != AVL_GET_BALANCE(node)) {
return 0;
}
if (((lh - rh) > 1) || ((lh - rh) < -1)) {
return 0;
}
return (1 + AVL_MAX (lh, rh));
}
}
static void
igloo_avl_verify_parent (igloo_avl_node * node, igloo_avl_node * parent)
{
if (node->parent != parent) {
return;
}
if (node->left) {
igloo_avl_verify_parent (node->left, node);
}
if (node->right) {
igloo_avl_verify_parent (node->right, node);
}
}
static long
igloo_avl_verify_rank (igloo_avl_node * node)
{
if (!node) {
return 0;
} else {
unsigned long num_left=0, num_right=0;
if (node->left) {
num_left = igloo_avl_verify_rank (node->left);
}
if (node->right) {
num_right = igloo_avl_verify_rank (node->right);
}
if (AVL_GET_RANK (node) != num_left + 1) {
fprintf (stderr, "invalid rank at node %ld\n", (long) node->key);
exit (1);
}
return (num_left + num_right + 1);
}
}
/* sanity-check the tree */
int
igloo_avl_verify (igloo_avl_tree * tree)
{
if (tree->length) {
igloo_avl_verify_balance (tree->root->right);
igloo_avl_verify_parent (tree->root->right, tree->root);
igloo_avl_verify_rank (tree->root->right);
}
return (0);
}
/*
* These structures are accumulated on the stack during print_tree
* and are used to keep track of the width and direction of each
* branch in the history of a particular line <node>.
*/
typedef struct _link_node {
struct _link_node * parent;
char direction;
int width;
} link_node;
static char igloo_balance_chars[3] = {'\\', '-', '/'};
static int
igloo_default_key_printer (char * buffer, void * key)
{
return snprintf (buffer, AVL_KEY_PRINTER_BUFLEN, "%p", key);
}
/*
* When traveling the family tree, a change in direction
* indicates when to print a connector. This is kinda crazy,
* we use the stack to build a linked list, and then travel
* it backwards using recursion.
*/
static void
igloo_print_connectors (link_node * link)
{
if (link->parent) {
igloo_print_connectors (link->parent);
}
if (link->parent && (link->parent->direction != link->direction) && (link->parent->parent)) {
int i;
fprintf (stdout, "|");
for (i=0; i < (link->width - 1); i++) {
fprintf (stdout, " ");
}
} else {
int i;
for (i=0; i < (link->width); i++) {
fprintf (stdout, " ");
}
}
}
/*
* The <key_printer> function writes a representation of the
* key into <buffer> (which is conveniently fixed in size to add
* the spice of danger). It should return the size of the
* representation.
*/
static void
igloo_print_node (igloo_avl_key_printer_fun_type key_printer,
igloo_avl_node * node,
link_node * link)
{
char buffer[AVL_KEY_PRINTER_BUFLEN];
unsigned int width;
width = key_printer (buffer, node->key);
if (node->right) {
link_node here;
here.parent = link;
here.direction = 1;
here.width = width + 11;
igloo_print_node (key_printer, node->right, &here);
}
igloo_print_connectors (link);
fprintf (stdout, "+-[%c %s %03d]",
igloo_balance_chars[AVL_GET_BALANCE(node)+1],
buffer,
(int)AVL_GET_RANK(node));
if (node->left || node->right) {
fprintf (stdout, "-|\n");
} else {
fprintf (stdout, "\n");
}
if (node->left) {
link_node here;
here.parent = link;
here.direction = -1;
here.width = width + 11;
igloo_print_node (key_printer, node->left, &here);
}
}
void
igloo_avl_print_tree (igloo_avl_tree * tree, igloo_avl_key_printer_fun_type key_printer)
{
link_node top = {NULL, 0, 0};
if (!key_printer) {
key_printer = igloo_default_key_printer;
}
if (tree->length) {
igloo_print_node (key_printer, tree->root->right, &top);
} else {
fprintf (stdout, "<empty tree>\n");
}
}
void igloo_avl_tree_rlock(igloo_avl_tree *tree)
{
thread_rwlock_rlock(&tree->rwlock);
}
void igloo_avl_tree_wlock(igloo_avl_tree *tree)
{
thread_rwlock_wlock(&tree->rwlock);
}
void igloo_avl_tree_unlock(igloo_avl_tree *tree)
{
thread_rwlock_unlock(&tree->rwlock);
}
#ifdef HAVE_AVL_NODE_LOCK
void avl_node_rlock(igloo_avl_node *node)
{
thread_rwlock_rlock(&node->rwlock);
}
void avl_node_wlock(igloo_avl_node *node)
{
thread_rwlock_wlock(&node->rwlock);
}
void avl_node_unlock(igloo_avl_node *node)
{
thread_rwlock_unlock(&node->rwlock);
}
#endif