/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de)
* IPv4 performance optimization (c) 2006, sven-ola(gmx.de)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of olsr.org, olsrd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Visit http://www.olsr.org for more information.
*
* If you find this software useful feel free to make a donation
* to the project. For more information see the website or contact
* the copyright holders.
*
* $Id: lq_avl.c,v 1.13 2007/09/05 16:30:50 bernd67 Exp $
*/
#include <stddef.h>
#include <time.h>
#include <string.h>
#include "lq_avl.h"
#define AVLMAX(x, y) ((x > y) ? x : y)
#define AVLMIN(x, y) ((x < y) ? x : y)
/*
* default comparison pointers
* set to the respective compare function.
* if avl_comp_default is set to zero, a fast
* inline ipv4 comparison will be executed.
*/
int (*avl_comp_default)(void *, void *) = NULL;
int (*avl_comp_prefix_default)(void *, void *);
int avl_comp_ipv4(void *ip1, void *ip2)
{
return(*(unsigned int *)ip1 == *(unsigned int *)ip2 ? 0 : \
*(unsigned int *)ip1 < *(unsigned int *)ip2 ? -1 : +1);
}
int avl_comp_ipv6(void *ip1, void *ip2)
{
return memcmp(ip1, ip2, 16);
}
void avl_init(struct avl_tree *tree, int (*comp)(void *, void *))
{
tree->root = NULL;
tree->first = NULL;
tree->last = NULL;
tree->count = 0;
tree->comp = comp;
}
static struct avl_node *avl_find_rec_ipv4(struct avl_node *node, void *key)
{
if (*(unsigned int *)key < *(unsigned int *)node->key)
{
if (node->left != NULL)
return avl_find_rec_ipv4(node->left, key);
}
else if (*(unsigned int *)key > *(unsigned int *)node->key)
{
if (node->right != NULL)
return avl_find_rec_ipv4(node->right, key);
}
return node;
}
static struct avl_node *avl_find_rec(struct avl_node *node, void *key,
int (*comp)(void *, void *))
{
int diff;
if (0 == comp)
return avl_find_rec_ipv4(node, key);
diff = (*comp)(key, node->key);
if (diff < 0)
{
if (node->left != NULL)
return avl_find_rec(node->left, key, comp);
return node;
}
if (diff > 0)
{
if (node->right != NULL)
return avl_find_rec(node->right, key, comp);
return node;
}
return node;
}
struct avl_node *avl_find(struct avl_tree *tree, void *key)
{
struct avl_node *node;
if (tree->root == NULL)
return NULL;
node = avl_find_rec(tree->root, key, tree->comp);
if (0 == tree->comp)
{
if (0 != inline_avl_comp_ipv4(node->key, key))
return NULL;
}
else
{
if ((*tree->comp)(node->key, key) != 0)
return NULL;
}
return node;
}
static void avl_rotate_right(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *left, *parent;
left = node->left;
parent = node->parent;
left->parent = parent;
node->parent = left;
if (parent == NULL)
tree->root = left;
else
{
if (parent->left == node)
parent->left = left;
else
parent->right = left;
}
node->left = left->right;
left->right = node;
if (node->left != NULL)
node->left->parent = node;
node->balance += 1 - AVLMIN(left->balance, 0);
left->balance += 1 + AVLMAX(node->balance, 0);
}
static void avl_rotate_left(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *right, *parent;
right = node->right;
parent = node->parent;
right->parent = parent;
node->parent = right;
if (parent == NULL)
tree->root = right;
else
{
if (parent->left == node)
parent->left = right;
else
parent->right = right;
}
node->right = right->left;
right->left = node;
if (node->right != NULL)
node->right->parent = node;
node->balance -= 1 + AVLMAX(right->balance, 0);
right->balance -= 1 - AVLMIN(node->balance, 0);
}
static void post_insert(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *parent;
if ((parent = node->parent) == NULL)
return;
if (node == parent->left)
{
parent->balance--;
if (parent->balance == 0)
return;
if (parent->balance == -1)
{
post_insert(tree, parent);
return;
}
if (node->balance == -1)
{
avl_rotate_right(tree, parent);
return;
}
avl_rotate_left(tree, node);
avl_rotate_right(tree, node->parent->parent);
return;
}
parent->balance++;
if (parent->balance == 0)
return;
if (parent->balance == 1)
{
post_insert(tree, parent);
return;
}
if (node->balance == 1)
{
avl_rotate_left(tree, parent);
return;
}
avl_rotate_right(tree, node);
avl_rotate_left(tree, node->parent->parent);
return;
}
static void avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node,
struct avl_node *node)
{
if (pos_node->prev != NULL)
pos_node->prev->next = node;
else
tree->first = node;
node->prev = pos_node->prev;
node->next = pos_node;
pos_node->prev = node;
tree->count++;
}
static void avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node,
struct avl_node *node)
{
if (pos_node->next != NULL)
pos_node->next->prev = node;
else
tree->last = node;
node->prev = pos_node;
node->next = pos_node->next;
pos_node->next = node;
tree->count++;
}
static void avl_remove(struct avl_tree *tree, struct avl_node *node)
{
if (node->prev != NULL)
node->prev->next = node->next;
else
tree->first = node->next;
if (node->next != NULL)
node->next->prev = node->prev;
else
tree->last = node->prev;
tree->count--;
}
int avl_insert(struct avl_tree *tree, struct avl_node *new, int allow_duplicates)
{
struct avl_node *node;
struct avl_node *last;
int diff;
new->parent = NULL;
new->left = NULL;
new->right = NULL;
new->next = NULL;
new->prev = NULL;
new->balance = 0;
new->leader = 1;
if (tree->root == NULL)
{
tree->root = new;
tree->first = new;
tree->last = new;
tree->count = 1;
return 0;
}
node = avl_find_rec(tree->root, new->key, tree->comp);
last = node;
while (last->next != NULL && last->next->leader == 0)
last = last->next;
if (0 == tree->comp)
diff = inline_avl_comp_ipv4(new->key, node->key);
else
diff = (*tree->comp)(new->key, node->key);
if (diff == 0)
{
if (allow_duplicates == AVL_DUP_NO)
return -1;
new->leader = 0;
avl_insert_after(tree, last, new);
return 0;
}
if (node->balance == 1)
{
avl_insert_before(tree, node, new);
node->balance = 0;
new->parent = node;
node->left = new;
return 0;
}
if (node->balance == -1)
{
avl_insert_after(tree, last, new);
node->balance = 0;
new->parent = node;
node->right = new;
return 0;
}
if (diff < 0)
{
avl_insert_before(tree, node, new);
node->balance = -1;
new->parent = node;
node->left = new;
post_insert(tree, node);
return 0;
}
avl_insert_after(tree, last, new);
node->balance = 1;
new->parent = node;
node->right = new;
post_insert(tree, node);
return 0;
}
static void avl_post_delete(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *parent;
if ((parent = node->parent) == NULL)
return;
if (node == parent->left)
{
parent->balance++;
if (parent->balance == 0)
{
avl_post_delete(tree, parent);
return;
}
if (parent->balance == 1)
return;
if (parent->right->balance == 0)
{
avl_rotate_left(tree, parent);
return;
}
if (parent->right->balance == 1)
{
avl_rotate_left(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
avl_rotate_right(tree, parent->right);
avl_rotate_left(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
parent->balance--;
if (parent->balance == 0)
{
avl_post_delete(tree, parent);
return;
}
if (parent->balance == -1)
return;
if (parent->left->balance == 0)
{
avl_rotate_right(tree, parent);
return;
}
if (parent->left->balance == -1)
{
avl_rotate_right(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
avl_rotate_left(tree, parent->left);
avl_rotate_right(tree, parent);
avl_post_delete(tree, parent->parent);
}
static struct avl_node *avl_local_min(struct avl_node *node)
{
while (node->left != NULL)
node = node->left;
return node;
}
#if 0
static struct avl_node *avl_local_max(struct avl_node *node)
{
while (node->right != NULL)
node = node->right;
return node;
}
#endif
static void avl_delete_worker(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *parent, *min;
parent = node->parent;
if (node->left == NULL && node->right == NULL)
{
if (parent == NULL)
{
tree->root = NULL;
return;
}
if (parent->left == node)
{
parent->left = NULL;
parent->balance++;
if (parent->balance == 1)
return;
if (parent->balance == 0)
{
avl_post_delete(tree, parent);
return;
}
if (parent->right->balance == 0)
{
avl_rotate_left(tree, parent);
return;
}
if (parent->right->balance == 1)
{
avl_rotate_left(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
avl_rotate_right(tree, parent->right);
avl_rotate_left(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
if (parent->right == node)
{
parent->right = NULL;
parent->balance--;
if (parent->balance == -1)
return;
if (parent->balance == 0)
{
avl_post_delete(tree, parent);
return;
}
if (parent->left->balance == 0)
{
avl_rotate_right(tree, parent);
return;
}
if (parent->left->balance == -1)
{
avl_rotate_right(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
avl_rotate_left(tree, parent->left);
avl_rotate_right(tree, parent);
avl_post_delete(tree, parent->parent);
return;
}
}
if (node->left == NULL)
{
if (parent == NULL)
{
tree->root = node->right;
node->right->parent = NULL;
return;
}
node->right->parent = parent;
if (parent->left == node)
parent->left = node->right;
else
parent->right = node->right;
avl_post_delete(tree, node->right);
return;
}
if (node->right == NULL)
{
if (parent == NULL)
{
tree->root = node->left;
node->left->parent = NULL;
return;
}
node->left->parent = parent;
if (parent->left == node)
parent->left = node->left;
else
parent->right = node->left;
avl_post_delete(tree, node->left);
return;
}
min = avl_local_min(node->right);
avl_delete_worker(tree, min);
parent = node->parent;
min->balance = node->balance;
min->parent = parent;
min->left = node->left;
min->right = node->right;
if (min->left != NULL)
min->left->parent = min;
if (min->right != NULL)
min->right->parent = min;
if (parent == NULL)
{
tree->root = min;
return;
}
if (parent->left == node)
{
parent->left = min;
return;
}
parent->right = min;
}
void avl_delete(struct avl_tree *tree, struct avl_node *node)
{
struct avl_node *next;
struct avl_node *parent;
struct avl_node *left;
struct avl_node *right;
if (node->leader != 0)
{
next = node->next;
if (next != NULL && next->leader == 0)
{
next->leader = 1;
next->balance = node->balance;
parent = node->parent;
left = node->left;
right = node->right;
next->parent = parent;
next->left = left;
next->right = right;
if (parent == NULL)
tree->root = next;
else
{
if (node == parent->left)
parent->left = next;
else
parent->right = next;
}
if (left != NULL)
left->parent = next;
if (right != NULL)
right->parent = next;
}
else
avl_delete_worker(tree, node);
}
avl_remove(tree, node);
}
struct avl_node *avl_walk_first(struct avl_tree *tree)
{
return tree->first;
}
struct avl_node *avl_walk_last(struct avl_tree *tree)
{
return tree->last;
}
struct avl_node *avl_walk_next(struct avl_node *node)
{
return node->next;
}
struct avl_node *avl_walk_prev(struct avl_node *node)
{
return node->prev;
}
/*
* Local Variables:
* c-basic-offset: 2
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1