cf49b2c064
- Added some extra search functions to rbl routines - Fix subnet_lookup() - Reorder some syslog messages to make more sense - daemon() is back - Don't let scripts execute in parallel (gives race conditions, and anyway something MIGHT just be configured which is necessary for further execution of tinc itself) - Accidently merged check_child() with execute_script(). - Small fixes
596 lines
12 KiB
C
596 lines
12 KiB
C
/*
|
|
rbl.c -- red-black tree + linked list convenience
|
|
Copyright (C) 2000 Ivo Timmermans <itimmermans@bigfoot.com>,
|
|
2000 Guus Sliepen <guus@sliepen.warande.net>
|
|
|
|
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 2 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, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
$Id: rbl.c,v 1.1.2.12 2000/11/24 23:12:59 guus Exp $
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <xalloc.h>
|
|
|
|
#include "rbl.h"
|
|
#include <system.h>
|
|
|
|
/* Allocate a new rbl node */
|
|
rbl_t *new_rbl()
|
|
{
|
|
return (rbl_t *)xmalloc_and_zero(sizeof(rbl_t));
|
|
}
|
|
|
|
/* Free a rbl node */
|
|
void free_rbl(rbl_t *rbl)
|
|
{
|
|
if(rbl->data && rbl->tree->delete)
|
|
rbl->tree->delete(rbl->data);
|
|
free(rbl);
|
|
}
|
|
|
|
/* Allocate a new rbltree header */
|
|
rbltree_t *new_rbltree(rbl_compare_t compare, rbl_action_t delete)
|
|
{
|
|
rbltree_t *tree;
|
|
|
|
tree = (rbltree_t *)xmalloc_and_zero(sizeof(rbltree_t));
|
|
if(tree)
|
|
{
|
|
tree->compare = compare;
|
|
tree->delete = delete;
|
|
}
|
|
|
|
return tree;
|
|
}
|
|
|
|
/* Free a rbltree header */
|
|
void free_rbltree(rbltree_t *tree)
|
|
{
|
|
free(tree);
|
|
}
|
|
|
|
/* Search closest match in the tree */
|
|
rbl_t *rbl_search_closest_rbl(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl, *next;
|
|
int result;
|
|
|
|
next = rbl = tree->top;
|
|
|
|
while(next)
|
|
{
|
|
rbl = next;
|
|
|
|
result = tree->compare(data, rbl->data);
|
|
|
|
if(result < 0)
|
|
next = rbl->left;
|
|
else if(result > 0)
|
|
next = rbl->right;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return rbl;
|
|
}
|
|
|
|
/* Search closest match in the tree */
|
|
rbl_t *rbl_search_closest_greater_rbl(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_closest_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
{
|
|
if(tree->compare(data, rbl->data) > 0)
|
|
rbl = rbl->next;
|
|
}
|
|
|
|
return rbl;
|
|
}
|
|
|
|
/* Search closest match in the tree */
|
|
rbl_t *rbl_search_closest_smaller_rbl(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_closest_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
{
|
|
if(tree->compare(data, rbl->data) < 0)
|
|
rbl = rbl->next;
|
|
}
|
|
|
|
return rbl;
|
|
}
|
|
|
|
void *rbl_search_closest(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_closest_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
return rbl->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void *rbl_search_closest_greater(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_closest_greater_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
return rbl->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void *rbl_search_closest_smaller(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_closest_smaller_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
return rbl->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/* Search exact match or return NULL pointer */
|
|
rbl_t *rbl_search_rbl(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
int result;
|
|
|
|
rbl = tree->top;
|
|
|
|
while(rbl)
|
|
{
|
|
result = tree->compare(data, rbl->data);
|
|
|
|
if(result < 0)
|
|
rbl = rbl->left;
|
|
else if(result > 0)
|
|
rbl = rbl->right;
|
|
else
|
|
return rbl;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *rbl_search(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
return rbl->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/* Red-black tree operations taken from Introduction to Algorithms,
|
|
Cormen, Leiserson & Rivest, chapter 14.
|
|
*/
|
|
|
|
void rbl_left_rotate(rbl_t *x)
|
|
{
|
|
rbl_t *y;
|
|
|
|
y = x->right;
|
|
x->right = y->left;
|
|
|
|
if(y->left)
|
|
y->left->parent = x;
|
|
|
|
y->parent = x->parent;
|
|
|
|
if(!x->parent)
|
|
x->tree->top = y;
|
|
else
|
|
if(x == x->parent->left)
|
|
x->parent->left = y;
|
|
else
|
|
x->parent->right = y;
|
|
|
|
y->left = x;
|
|
x->parent = y;
|
|
}
|
|
|
|
void rbl_right_rotate(rbl_t *y)
|
|
{
|
|
rbl_t *x;
|
|
|
|
x = y->left;
|
|
y->left = x->right;
|
|
|
|
if(x->right)
|
|
x->right->parent = y;
|
|
|
|
x->parent = y->parent;
|
|
|
|
if(!y->parent)
|
|
y->tree->top = x;
|
|
else
|
|
if(y == y->parent->right)
|
|
y->parent->right = x;
|
|
else
|
|
y->parent->left = x;
|
|
|
|
x->right = y;
|
|
y->parent = x;
|
|
}
|
|
|
|
/* Insert a node into the rbl tree */
|
|
rbl_t *rbl_insert_rbl(rbltree_t *tree, rbl_t *rbl)
|
|
{
|
|
rbl_t *closest, *x, *y;
|
|
int result;
|
|
|
|
rbl->tree = tree;
|
|
|
|
/* Binary tree and linked list insert */
|
|
|
|
if(tree->top)
|
|
{
|
|
closest = rbl_search_closest_rbl(tree, rbl->data);
|
|
result = tree->compare(rbl->data, closest->data);
|
|
if(result < 0)
|
|
{
|
|
closest->left = rbl;
|
|
|
|
rbl->prev = closest->prev;
|
|
rbl->next = closest;
|
|
closest->prev = rbl;
|
|
|
|
if(rbl->prev)
|
|
rbl->prev->next = rbl;
|
|
else
|
|
tree->head = rbl;
|
|
}
|
|
else if(result > 0)
|
|
{
|
|
closest->right = rbl;
|
|
|
|
rbl->next = closest->next;
|
|
rbl->prev = closest;
|
|
closest->next = rbl;
|
|
|
|
if(rbl->next)
|
|
rbl->next->prev = rbl;
|
|
else
|
|
tree->tail = rbl;
|
|
}
|
|
else
|
|
return closest; /* Ofcourse, we cannot add two identical things */
|
|
|
|
rbl->parent = closest;
|
|
}
|
|
else
|
|
{
|
|
tree->top = rbl;
|
|
tree->head = rbl;
|
|
tree->tail = rbl;
|
|
}
|
|
|
|
/* Red-black part of insert */
|
|
|
|
x = rbl;
|
|
x->color = RBL_RED;
|
|
|
|
while(x != tree->top && x->parent->color == RBL_RED)
|
|
{
|
|
if(x->parent == x->parent->parent->left)
|
|
{
|
|
y = x->parent->parent->right;
|
|
if(y && y->color == RBL_RED)
|
|
{
|
|
x->parent->color = RBL_BLACK;
|
|
y->color = RBL_BLACK;
|
|
x->parent->parent->color = RBL_RED;
|
|
x = x->parent->parent;
|
|
}
|
|
else
|
|
{
|
|
if(x == x->parent->right)
|
|
{
|
|
x = x->parent;
|
|
rbl_left_rotate(x);
|
|
}
|
|
x->parent->color = RBL_BLACK;
|
|
x->parent->parent->color = RBL_RED;
|
|
rbl_right_rotate(x->parent->parent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
y = x->parent->parent->left;
|
|
if(y && y->color == RBL_RED)
|
|
{
|
|
x->parent->color = RBL_BLACK;
|
|
y->color = RBL_BLACK;
|
|
x->parent->parent->color = RBL_RED;
|
|
x = x->parent->parent;
|
|
}
|
|
else
|
|
{
|
|
if(x == x->parent->left)
|
|
{
|
|
x = x->parent;
|
|
rbl_right_rotate(x);
|
|
}
|
|
x->parent->color = RBL_BLACK;
|
|
x->parent->parent->color = RBL_RED;
|
|
rbl_left_rotate(x->parent->parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
tree->top->color = RBL_BLACK;
|
|
return rbl;
|
|
}
|
|
|
|
/* Create a new node and insert it into the tree */
|
|
rbl_t *rbl_insert(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = new_rbl();
|
|
rbl->data = data;
|
|
|
|
if(rbl_insert_rbl(tree, rbl) == rbl)
|
|
return rbl;
|
|
else
|
|
{
|
|
free_rbl(rbl);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Restore red-black property after violation due to a deletion */
|
|
void rbl_delete_fixup(rbl_t *x)
|
|
{
|
|
rbl_t *w;
|
|
|
|
while(x != x->tree->top && x->color == RBL_BLACK)
|
|
{
|
|
if(x == x->parent->left)
|
|
{
|
|
w = x->parent->right;
|
|
if(w->color == RBL_RED)
|
|
{
|
|
w->color = RBL_BLACK;
|
|
x->parent->color = RBL_RED;
|
|
rbl_left_rotate(x->parent);
|
|
w = x->parent->right;
|
|
}
|
|
if(w->left->color == RBL_BLACK && w->right->color == RBL_BLACK)
|
|
{
|
|
w->color = RBL_RED;
|
|
x = x->parent;
|
|
}
|
|
else
|
|
{
|
|
if(w->right->color == RBL_BLACK)
|
|
{
|
|
w->left->color = RBL_BLACK;
|
|
w->color = RBL_RED;
|
|
rbl_right_rotate(w);
|
|
w = x->parent->right;
|
|
}
|
|
w->color = x->parent->color;
|
|
x->parent->color = RBL_BLACK;
|
|
w->right->color = RBL_BLACK;
|
|
rbl_left_rotate(x->parent);
|
|
x = x->tree->top;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
w = x->parent->left;
|
|
if(w->color == RBL_RED)
|
|
{
|
|
w->color = RBL_BLACK;
|
|
x->parent->color = RBL_RED;
|
|
rbl_right_rotate(x->parent);
|
|
w = x->parent->left;
|
|
}
|
|
if(w->right->color == RBL_BLACK && w->left->color == RBL_BLACK)
|
|
{
|
|
w->color = RBL_RED;
|
|
x = x->parent;
|
|
}
|
|
else
|
|
{
|
|
if(w->left->color == RBL_BLACK)
|
|
{
|
|
w->right->color = RBL_BLACK;
|
|
w->color = RBL_RED;
|
|
rbl_left_rotate(w);
|
|
w = x->parent->left;
|
|
}
|
|
w->color = x->parent->color;
|
|
x->parent->color = RBL_BLACK;
|
|
w->left->color = RBL_BLACK;
|
|
rbl_right_rotate(x->parent);
|
|
x = x->tree->top;
|
|
}
|
|
}
|
|
}
|
|
|
|
x->color = RBL_BLACK;
|
|
}
|
|
|
|
/* Unlink node from the tree, but keep the node intact. */
|
|
rbl_t *rbl_unlink_rbl(rbl_t *rbl)
|
|
{
|
|
rbl_t *x, *y;
|
|
|
|
/* Binary tree delete */
|
|
|
|
if(rbl->left && rbl->right)
|
|
y = rbl->next;
|
|
else
|
|
y = rbl;
|
|
|
|
if(y->left)
|
|
x = y->left;
|
|
else
|
|
x = y->right;
|
|
|
|
if(x)
|
|
x->parent = y->parent;
|
|
|
|
if(!y->parent)
|
|
rbl->tree->top = x;
|
|
else
|
|
if(y == y->parent->left)
|
|
y->parent->left = x;
|
|
else
|
|
y->parent->right = x;
|
|
|
|
if(y != rbl)
|
|
{
|
|
y->left = rbl->left;
|
|
y->right = rbl->right;
|
|
y->parent = rbl->parent;
|
|
if(rbl == rbl->parent->left)
|
|
rbl->parent->left = y;
|
|
else
|
|
rbl->parent->right = y;
|
|
}
|
|
|
|
/* Linked list delete */
|
|
|
|
if(rbl->prev)
|
|
rbl->prev->next = rbl->next;
|
|
else
|
|
rbl->tree->head = rbl->next;
|
|
|
|
if(rbl->next)
|
|
rbl->next->prev = rbl->prev;
|
|
else
|
|
rbl->tree->tail = rbl->prev;
|
|
|
|
/* Red-black part of delete */
|
|
|
|
if(y->color == RBL_BLACK && x)
|
|
rbl_delete_fixup(x);
|
|
|
|
return rbl;
|
|
}
|
|
|
|
/* Search node in tree and unlink it */
|
|
rbl_t *rbl_unlink(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_search_rbl(tree, data);
|
|
|
|
if(rbl)
|
|
rbl_unlink_rbl(rbl);
|
|
|
|
return rbl;
|
|
}
|
|
|
|
/* Unlink node and free it */
|
|
void rbl_delete_rbl(rbl_t *rbl)
|
|
{
|
|
rbl_unlink_rbl(rbl);
|
|
free_rbl(rbl);
|
|
}
|
|
|
|
/* Search node in tree, unlink and free it */
|
|
void rbl_delete(rbltree_t *tree, void *data)
|
|
{
|
|
rbl_t *rbl;
|
|
|
|
rbl = rbl_unlink(tree, data);
|
|
|
|
if(rbl)
|
|
free_rbl(rbl);
|
|
}
|
|
|
|
/* Optimized unlinking for a complete tree */
|
|
void rbl_unlink_rbltree(rbltree_t *tree)
|
|
{
|
|
rbl_t *rbl, *next;
|
|
|
|
for(rbl = tree->head; rbl; rbl = next)
|
|
{
|
|
next = rbl->next;
|
|
rbl->tree = NULL;
|
|
rbl->parent = NULL;
|
|
rbl->left = NULL;
|
|
rbl->right = NULL;
|
|
rbl->prev = NULL;
|
|
rbl->next = NULL;
|
|
}
|
|
|
|
tree->top = NULL;
|
|
tree->head = NULL;
|
|
tree->tail = NULL;
|
|
}
|
|
|
|
/* Optimized deletion for a complete tree */
|
|
void rbl_delete_rbltree(rbltree_t *tree)
|
|
{
|
|
rbl_t *rbl, *next;
|
|
|
|
for(rbl = tree->head; rbl; rbl = next)
|
|
{
|
|
next = rbl->next;
|
|
free_rbl(rbl);
|
|
}
|
|
|
|
tree->top = NULL;
|
|
tree->head = NULL;
|
|
tree->tail = NULL;
|
|
}
|
|
|
|
/* Do action for each list entry (in order)
|
|
Deletion of entry for which action is called is allowed.
|
|
*/
|
|
void rbl_foreach(rbltree_t *tree, rbl_action_t action)
|
|
{
|
|
rbl_t *rbl, *next;
|
|
|
|
for(rbl = tree->head; rbl; rbl = next)
|
|
{
|
|
next = rbl->next;
|
|
action(rbl->data);
|
|
}
|
|
}
|
|
|
|
void rbl_foreach_rbl(rbltree_t *tree, rbl_action_rbl_t action)
|
|
{
|
|
rbl_t *rbl, *next;
|
|
|
|
for(rbl = tree->head; rbl; rbl = next)
|
|
{
|
|
next = rbl->next;
|
|
action(rbl);
|
|
}
|
|
}
|