1999-01-20 10:13:43 +00:00
|
|
|
/*
|
1999-01-25 15:46:30 +00:00
|
|
|
* Copyright (C) 1999 Internet Software Consortium.
|
1999-01-20 10:13:43 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
1999-01-22 04:35:11 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <isc/assertions.h>
|
|
|
|
#include <isc/error.h>
|
|
|
|
#include <isc/mem.h>
|
1999-01-25 15:46:30 +00:00
|
|
|
#include <isc/result.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
#include <dns/rbt.h>
|
|
|
|
#include <dns/result.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
#define RBT_MAGIC 0x5242542BU /* RBT+. */
|
|
|
|
#define VALID_RBT(rbt) ((rbt) != NULL && (rbt)->magic == RBT_MAGIC)
|
|
|
|
|
|
|
|
struct dns_rbt {
|
|
|
|
unsigned int magic;
|
|
|
|
isc_mem_t * mctx;
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbtnode_t * root;
|
|
|
|
dns_rbtnode_t * ancestors[256]; /* @@@ should be dynamic */
|
|
|
|
int ancestor_count;
|
|
|
|
/*
|
|
|
|
* The maximum number of labels in a name is 128; need space for 127
|
|
|
|
* to be able to store the down pointer history for the worst case.
|
|
|
|
*/
|
|
|
|
dns_rbtnode_t * levels[127];
|
|
|
|
int level_count;
|
1999-01-20 10:13:43 +00:00
|
|
|
};
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
|
|
#endif
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
#define LEFT(node) ((node)->left)
|
|
|
|
#define RIGHT(node) ((node)->right)
|
|
|
|
#define DOWN(node) ((node)->down)
|
|
|
|
#define NAMELEN(node) ((node)->name_length)
|
|
|
|
#define NAME(node) ((void *)((node) + 1))
|
|
|
|
#define DATA(node) ((node)->data)
|
|
|
|
#define COLOR(node) ((node)->color)
|
|
|
|
|
|
|
|
#define SET_COLOR(node, value) ((node)->color = (value))
|
|
|
|
#define SET_LEFT(node, child) ((node)->left = (child))
|
|
|
|
#define SET_RIGHT(node, child) ((node)->right = (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)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the return value of cmp_names_for_depth().
|
|
|
|
*/
|
|
|
|
#define BOTH_ARE_EQUAL 0
|
|
|
|
#define FIRST_IS_LESS -1
|
|
|
|
#define FIRST_IS_MORE -2
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define inline
|
|
|
|
/*
|
|
|
|
* A little something to help out in GDB.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
isc_region_t Name(dns_rbtnode_t *node);
|
1999-01-20 10:13:43 +00:00
|
|
|
isc_region_t
|
1999-01-25 15:46:30 +00:00
|
|
|
Name(dns_rbtnode_t *node) {
|
1999-01-20 10:13:43 +00:00
|
|
|
isc_region_t r;
|
|
|
|
|
|
|
|
r.length = NAMELEN(node);
|
|
|
|
r.base = NAME(node);
|
|
|
|
|
|
|
|
return(r);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Forward declarations.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
static dns_result_t create_node(isc_mem_t *mctx,
|
|
|
|
dns_name_t *name, dns_rbtnode_t **nodep);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
static int cmp_label(dns_label_t *a, dns_label_t *b);
|
|
|
|
static inline int cmp_names_on_level(dns_name_t *a, dns_name_t *b);
|
|
|
|
static inline int cmp_names_for_depth(dns_name_t *a, dns_name_t *b);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
static inline void rotate_left(dns_rbtnode_t *node, dns_rbtnode_t *parent,
|
|
|
|
dns_rbtnode_t **rootp);
|
|
|
|
static inline void rotate_right(dns_rbtnode_t *node, dns_rbtnode_t *parent,
|
|
|
|
dns_rbtnode_t **rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
static dns_result_t dns_rbt_addnode(dns_rbtnode_t *node,
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t **rootp);
|
1999-01-27 01:48:55 +00:00
|
|
|
static dns_result_t dns_rbt_delete_workhorse(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *delete);
|
1999-01-25 15:46:30 +00:00
|
|
|
static void dns_rbt_deletetree(isc_mem_t *mctx,
|
|
|
|
dns_rbtnode_t *node, dns_rbtnode_t **root);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a red/black tree of trees.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_rbt_create(isc_mem_t *mctx, dns_rbt_t **rbtp) {
|
|
|
|
dns_rbt_t *rbt;
|
|
|
|
|
|
|
|
REQUIRE(mctx != NULL);
|
|
|
|
REQUIRE(rbtp != NULL && *rbtp == NULL);
|
|
|
|
|
|
|
|
rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
|
|
|
|
if (rbt == NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_NOMEMORY);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
rbt->mctx = mctx;
|
|
|
|
rbt->root = NULL;
|
|
|
|
rbt->magic = RBT_MAGIC;
|
|
|
|
|
|
|
|
*rbtp = rbt;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a red/black tree of trees.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dns_rbt_destroy(dns_rbt_t **rbtp) {
|
|
|
|
dns_rbt_t *rbt;
|
|
|
|
|
|
|
|
REQUIRE(rbtp != NULL && VALID_RBT(*rbtp));
|
|
|
|
|
|
|
|
rbt = *rbtp;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_deletetree(rbt->mctx, rbt->root, &rbt->root);
|
|
|
|
|
|
|
|
rbt->magic = 0;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
isc_mem_put(rbt->mctx, rbt, sizeof(*rbt));
|
|
|
|
|
1999-01-26 03:31:53 +00:00
|
|
|
#ifdef ISC_MEM_DEBUG
|
|
|
|
isc_mem_stats(rbt->mctx, stderr);
|
|
|
|
#endif
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
*rbtp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add 'name' to tree, initializing its data pointer with 'data'.
|
|
|
|
*/
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t
|
1999-01-26 03:31:53 +00:00
|
|
|
dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t **root, *current, *child, *new_node, *new_current, *parent;
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_t add_name, current_name, new_name, tmp_name;
|
|
|
|
int compared, add_labels, current_labels, keep_labels, start_label;
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t result;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a copy of the name so the original name structure is
|
|
|
|
* not modified.
|
|
|
|
*/
|
|
|
|
memcpy(&add_name, name, sizeof(add_name));
|
|
|
|
|
|
|
|
/* @@@
|
|
|
|
* The following code nearly duplicates a non-trivial
|
1999-01-27 01:48:55 +00:00
|
|
|
* amount of the dns_rbt_addnode algorithm. It can be
|
1999-01-20 10:13:43 +00:00
|
|
|
* improved by merging the two functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (rbt->root == NULL) {
|
|
|
|
result = create_node(rbt->mctx, &add_name, &new_node);
|
1999-01-25 15:46:30 +00:00
|
|
|
rbt->root = new_node;
|
1999-01-20 10:13:43 +00:00
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
root = &rbt->root;
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
child = *root;
|
|
|
|
dns_name_init(¤t_name, NULL);
|
|
|
|
do {
|
|
|
|
current = child;
|
|
|
|
|
|
|
|
dns_rbt_namefromnode(current, ¤t_name);
|
|
|
|
compared = cmp_names_for_depth(&add_name, ¤t_name);
|
|
|
|
|
|
|
|
if (compared == BOTH_ARE_EQUAL)
|
|
|
|
if (DATA(current) != NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
return(DNS_R_EXISTS); /* @@@ DNS_R_DISALLOWED */
|
1999-01-20 10:13:43 +00:00
|
|
|
else {
|
1999-01-25 15:46:30 +00:00
|
|
|
DATA(current) = data;
|
|
|
|
return(DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
else if (compared == FIRST_IS_LESS) {
|
|
|
|
parent = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
child = LEFT(current);
|
1999-01-25 15:46:30 +00:00
|
|
|
} else if (compared == FIRST_IS_MORE) {
|
|
|
|
parent = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
child = RIGHT(current);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
} else {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* This name has some suffix in common with the
|
|
|
|
* name at the current node. If the name at
|
|
|
|
* the current node is shorter, that means the
|
|
|
|
* new name should be in a subtree. If the
|
|
|
|
* name at the current node is longer, that means
|
|
|
|
* the down pointer to this tree should point
|
|
|
|
* to a new tree that has the common suffix, and
|
|
|
|
* the non-common parts of these two names should
|
|
|
|
* start a new tree.
|
|
|
|
*/
|
|
|
|
add_labels = dns_name_countlabels(&add_name);
|
|
|
|
current_labels = dns_name_countlabels(¤t_name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When *root == rbt->root, the current tree level is
|
|
|
|
* the top of the tree of trees, and the root label is
|
|
|
|
* not counted in this module.
|
|
|
|
*/
|
|
|
|
if (*root == rbt->root)
|
|
|
|
add_labels--, current_labels--;
|
|
|
|
|
|
|
|
if (compared == current_labels) {
|
|
|
|
/*
|
|
|
|
* All of the exising labels are in common,
|
|
|
|
* so the new name is in a subtree.
|
|
|
|
* First, turn the non-in-common part of
|
|
|
|
* &add_name into its own dns_name_t to be
|
|
|
|
* searched for in the downtree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
start_label = 0;
|
|
|
|
keep_labels = add_labels - compared;
|
|
|
|
|
|
|
|
dns_name_init(&new_name, NULL);
|
|
|
|
dns_name_getlabelsequence(&add_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&add_name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Follow the down pointer (possibly NULL).
|
|
|
|
*/
|
|
|
|
root = &DOWN(current);
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = NULL;
|
|
|
|
child = DOWN(current);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The number of labels in common is fewer
|
|
|
|
* than the number of labels at the current
|
|
|
|
* node, so the current node must be adjusted
|
|
|
|
* to have just the common suffix, and a down
|
|
|
|
* pointer made to a new tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the in common labels of the current
|
|
|
|
* name. If this is part of the top level
|
|
|
|
* tree, then the root label needs to be
|
|
|
|
* kept in the name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
start_label = current_labels - compared;
|
|
|
|
keep_labels = compared + (*root == rbt->root);
|
|
|
|
|
|
|
|
dns_name_init(&tmp_name, NULL);
|
|
|
|
dns_name_getlabelsequence(¤t_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&tmp_name);
|
|
|
|
|
|
|
|
result = create_node(rbt->mctx,
|
|
|
|
&tmp_name, &new_current);
|
1999-01-25 15:46:30 +00:00
|
|
|
if (result != DNS_R_SUCCESS)
|
1999-01-20 10:13:43 +00:00
|
|
|
return (result);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reproduce the current node, but then
|
|
|
|
* fix its name length.
|
|
|
|
*/
|
|
|
|
memcpy(new_current, current, sizeof(*current));
|
|
|
|
NAMELEN(new_current) = tmp_name.length;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix pointers that were to the current node.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
if (parent != NULL)
|
|
|
|
if (LEFT(parent) == current)
|
|
|
|
SET_LEFT(parent, new_current);
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-01-25 15:46:30 +00:00
|
|
|
SET_RIGHT(parent, new_current);
|
1999-01-20 10:13:43 +00:00
|
|
|
if (*root == current)
|
|
|
|
*root = new_current;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now create the new root of the subtree
|
|
|
|
* as the not-in-common labels of the current
|
|
|
|
* node. Its down pointer and name data
|
|
|
|
* should be preserved, while left, right
|
|
|
|
* and parent pointers are nullified.
|
|
|
|
*/
|
|
|
|
|
|
|
|
start_label = 0;
|
|
|
|
keep_labels = current_labels - compared;
|
|
|
|
|
|
|
|
dns_name_init(&new_name, NULL);
|
|
|
|
dns_name_getlabelsequence(¤t_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&new_name);
|
|
|
|
|
|
|
|
|
|
|
|
result = create_node(rbt->mctx,
|
|
|
|
&new_name, &new_node);
|
1999-01-25 15:46:30 +00:00
|
|
|
if (result != DNS_R_SUCCESS)
|
1999-01-20 10:13:43 +00:00
|
|
|
return (result);
|
|
|
|
|
|
|
|
DATA(new_node) = DATA(new_current);
|
|
|
|
DOWN(new_node) = DOWN(new_current);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that the old name in the existing
|
|
|
|
* node has been disected into two new
|
|
|
|
* nodes, the old node can be freed.
|
|
|
|
*/
|
|
|
|
isc_mem_put(rbt->mctx, current,
|
|
|
|
sizeof(*current) +
|
|
|
|
NAMELEN(current));
|
|
|
|
current = new_current;
|
|
|
|
|
|
|
|
/*
|
1999-01-25 15:46:30 +00:00
|
|
|
* Set up the new root of the next level.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
DOWN(current) = new_node;
|
1999-01-20 10:13:43 +00:00
|
|
|
root = &DOWN(current);
|
|
|
|
|
|
|
|
if (compared == add_labels) {
|
|
|
|
/*
|
|
|
|
* The name being added is a strict
|
|
|
|
* superset of the existing name,
|
|
|
|
* so the data coming in needs to be
|
|
|
|
* placed with the current node.
|
|
|
|
*/
|
|
|
|
DATA(current) = data;
|
|
|
|
/*
|
|
|
|
* Since the new name has had its
|
|
|
|
* data placed, the job is done!
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
return(DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The current node has no name data,
|
|
|
|
* because it is just a placeholder.
|
|
|
|
*/
|
|
|
|
DATA(current) = NULL;
|
|
|
|
/* The not-in-common parts of the new
|
|
|
|
* name will be inserted into the new
|
|
|
|
* level following this loop.
|
|
|
|
*/
|
|
|
|
start_label = 0;
|
|
|
|
keep_labels = add_labels - compared;
|
|
|
|
|
|
|
|
dns_name_init(&new_name, NULL);
|
|
|
|
dns_name_getlabelsequence(&add_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&add_name);
|
|
|
|
|
|
|
|
current = new_node;
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = NULL; /* @@@ ? only necessary if used outside loop */
|
1999-01-20 10:13:43 +00:00
|
|
|
child = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (child != NULL);
|
|
|
|
|
|
|
|
result = create_node(rbt->mctx,
|
|
|
|
&add_name, &new_node);
|
1999-01-25 15:46:30 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
1999-01-20 10:13:43 +00:00
|
|
|
DATA(new_node) = data;
|
1999-01-27 01:48:55 +00:00
|
|
|
result = dns_rbt_addnode(new_node, root);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the node for "name" in the tree of trees.
|
|
|
|
* If second argument "up" is non-NULL, set it to the node that has
|
|
|
|
* the down pointer for the found node.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t *
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t *current;
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_t *search_name, *new_search_name, *current_name;
|
|
|
|
dns_name_t holder1, holder2;
|
|
|
|
int compared, current_labels, keep_labels, dont_count_root_label;
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-01-20 10:13:43 +00:00
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search_name is the name segment being sought in each tree level.
|
|
|
|
*/
|
|
|
|
search_name = name;
|
|
|
|
|
|
|
|
current = rbt->root;
|
|
|
|
dont_count_root_label = 1;
|
|
|
|
|
|
|
|
dns_name_init(&holder1, NULL);
|
|
|
|
dns_name_init(&holder2, NULL);
|
|
|
|
|
|
|
|
current_name = &holder1;
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
rbt->level_count = 0;
|
|
|
|
rbt->ancestor_count = 0;
|
|
|
|
rbt->ancestors[rbt->ancestor_count++] = NULL;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
while (current != NULL) {
|
|
|
|
dns_rbt_namefromnode(current, current_name);
|
|
|
|
compared = cmp_names_for_depth(search_name, current_name);
|
|
|
|
|
|
|
|
if (compared == BOTH_ARE_EQUAL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Standard binary search tree movement.
|
|
|
|
*/
|
1999-01-27 01:48:55 +00:00
|
|
|
if (compared == FIRST_IS_LESS) {
|
|
|
|
rbt->ancestors[rbt->ancestor_count++] = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
current = LEFT(current);
|
1999-01-27 01:48:55 +00:00
|
|
|
} else if (compared == FIRST_IS_MORE) {
|
|
|
|
rbt->ancestors[rbt->ancestor_count++] = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
current = RIGHT(current);
|
1999-01-27 01:48:55 +00:00
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* The names have some common suffix labels.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* If the number in common are equal in length to the
|
|
|
|
* current node's name length (where the root label is
|
|
|
|
* not counted as part of the comparison) then follow
|
|
|
|
* the down pointer and search in the new tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
current_labels = dns_name_countlabels(current_name)
|
|
|
|
- dont_count_root_label;
|
|
|
|
|
|
|
|
if (compared == current_labels) {
|
|
|
|
/*
|
|
|
|
* Set up new name to search for as
|
|
|
|
* the not-in-common part.
|
|
|
|
*/
|
|
|
|
if (search_name == &holder2) {
|
|
|
|
new_search_name = &holder1;
|
|
|
|
current_name = &holder2;
|
|
|
|
} else {
|
|
|
|
new_search_name = &holder2;
|
|
|
|
current_name = &holder1;
|
|
|
|
}
|
|
|
|
|
|
|
|
keep_labels = dns_name_countlabels(search_name)
|
|
|
|
- dont_count_root_label
|
|
|
|
- compared;
|
|
|
|
|
|
|
|
dns_name_init(new_search_name, NULL);
|
|
|
|
dns_name_getlabelsequence(search_name,
|
|
|
|
0,
|
|
|
|
keep_labels,
|
|
|
|
new_search_name);
|
|
|
|
|
|
|
|
search_name = new_search_name;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search in the next tree level, which
|
|
|
|
* won't be the top level tree anymore, so
|
|
|
|
* there is no root label to ignore.
|
|
|
|
*/
|
1999-01-27 01:48:55 +00:00
|
|
|
rbt->ancestors[rbt->ancestor_count++] = NULL;
|
|
|
|
rbt->levels[rbt->level_count++] = current;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
current = DOWN(current);
|
|
|
|
dont_count_root_label = 0;
|
|
|
|
|
|
|
|
} else
|
|
|
|
/*
|
|
|
|
* Though there is a suffix in common, it
|
|
|
|
* isn't a down pointer, so the name does
|
|
|
|
* not exist.
|
|
|
|
*/
|
|
|
|
current = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
1999-01-26 03:31:53 +00:00
|
|
|
dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t *node;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
|
|
|
|
node = dns_rbt_findnode(rbt, name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (node != NULL && DATA(node) != NULL)
|
|
|
|
return(DATA(node));
|
|
|
|
else
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @@@ WHEN DELETING, IF A TREE IS LEFT WITH NO RIGHT OR LEFT NODES
|
|
|
|
THEN IT SHOULD HAVE ITS NAME GLOMMED INTO THE NAME ABOVE IT. THIS
|
|
|
|
COULD STAND A dns_name_prefix_name FUNCTION OR SOME SUCH. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a name from the tree of trees.
|
|
|
|
*
|
1999-01-27 01:48:55 +00:00
|
|
|
* This will remove all subnames of the name, too,
|
|
|
|
* and if this name is the last name in a level, the name
|
|
|
|
* one level up will be removed if it has no data associated with it.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t
|
|
|
|
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name) {
|
|
|
|
/*
|
1999-01-27 01:48:55 +00:00
|
|
|
* Find the node, building the ancestor chain.
|
|
|
|
* @@@ When searching, the name might not have an exact match:
|
1999-01-25 15:46:30 +00:00
|
|
|
* consider a.b.a.com, b.b.a.com and c.b.a.com as the only
|
|
|
|
* elements of a tree, which would make layer 1 a single
|
|
|
|
* node tree of "b.a.com" and layer 2 a three node tree of
|
1999-01-27 01:48:55 +00:00
|
|
|
* a, b, and c. Deleting a.com would find only a partial depth
|
1999-01-25 15:46:30 +00:00
|
|
|
* match in the first layer.
|
|
|
|
* deletes ALL subnames of the name.
|
1999-01-27 01:48:55 +00:00
|
|
|
* @@@ delete all ancestors that have no data???
|
1999-01-25 15:46:30 +00:00
|
|
|
*/
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbtnode_t *node;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
node = dns_rbt_findnode(rbt, name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (node != NULL) {
|
|
|
|
if (DOWN(node))
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbt_deletetree(rbt->mctx, DOWN(node), &DOWN(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbt_delete_workhorse(rbt, node);
|
|
|
|
isc_mem_put(rbt->mctx, node, sizeof(*node) + NAMELEN(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
return(DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
} else
|
1999-01-27 01:48:55 +00:00
|
|
|
return(DNS_R_NOTFOUND);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) {
|
1999-01-20 10:13:43 +00:00
|
|
|
isc_region_t r;
|
|
|
|
|
|
|
|
r.length = NAMELEN(node);
|
|
|
|
r.base = NAME(node);
|
|
|
|
|
|
|
|
dns_name_fromregion(name, &r);
|
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
static dns_result_t
|
|
|
|
create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
|
|
|
|
dns_rbtnode_t *node;
|
1999-01-20 10:13:43 +00:00
|
|
|
isc_region_t region;
|
|
|
|
|
|
|
|
dns_name_toregion(name, ®ion);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) + region.length);
|
1999-01-20 10:13:43 +00:00
|
|
|
if (node == NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_NOMEMORY);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
/* PARENT(node) = NULL; */
|
1999-01-20 10:13:43 +00:00
|
|
|
LEFT(node) = NULL;
|
|
|
|
RIGHT(node) = NULL;
|
|
|
|
DOWN(node) = NULL;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
MAKE_BLACK(node);
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
NAMELEN(node) = region.length;
|
|
|
|
memcpy(NAME(node), region.base, region.length);
|
|
|
|
|
|
|
|
*nodep = node;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp) {
|
|
|
|
dns_rbtnode_t *child;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(node != NULL);
|
|
|
|
REQUIRE(rootp != NULL);
|
|
|
|
|
|
|
|
child = RIGHT(node);
|
|
|
|
REQUIRE(child != NULL);
|
|
|
|
|
|
|
|
SET_RIGHT(node, LEFT(child));
|
|
|
|
SET_LEFT(child, node);
|
1999-01-25 15:46:30 +00:00
|
|
|
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
|
|
|
SET_LEFT(parent, child);
|
1999-01-20 10:13:43 +00:00
|
|
|
else {
|
1999-01-25 15:46:30 +00:00
|
|
|
SET_RIGHT(parent, child);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
*rootp = child;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_right(dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp) {
|
|
|
|
dns_rbtnode_t *child;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(node != NULL);
|
|
|
|
REQUIRE(rootp != NULL);
|
|
|
|
|
|
|
|
child = LEFT(node);
|
|
|
|
REQUIRE(child != NULL);
|
|
|
|
|
|
|
|
SET_LEFT(node, RIGHT(child));
|
|
|
|
SET_RIGHT(child, node);
|
1999-01-25 15:46:30 +00:00
|
|
|
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
|
|
|
SET_LEFT(parent, child);
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-01-25 15:46:30 +00:00
|
|
|
SET_RIGHT(parent, child);
|
1999-01-20 10:13:43 +00:00
|
|
|
} else
|
|
|
|
*rootp = child;
|
|
|
|
}
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This is the real workhorse of the insertion code, because it does the
|
|
|
|
* true red/black tree on a single level.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
static dns_result_t
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbt_addnode(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t *current, *child, *root, *tmp;
|
|
|
|
dns_rbtnode_t *ancestors[64], *parent, *grandparent; /* @@@ dynamic 64 */
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_t add_name, current_name;
|
|
|
|
dns_offsets_t offsets;
|
1999-01-25 15:46:30 +00:00
|
|
|
int i, depth;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(rootp != NULL);
|
|
|
|
REQUIRE(LEFT(node) == NULL && RIGHT(node) == NULL);
|
|
|
|
|
|
|
|
root = *rootp;
|
|
|
|
if (root == NULL) {
|
|
|
|
MAKE_BLACK(node);
|
|
|
|
*rootp = node;
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
current = NULL;
|
|
|
|
child = root;
|
1999-01-25 15:46:30 +00:00
|
|
|
depth = 0;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
dns_name_init(&add_name, offsets);
|
|
|
|
dns_rbt_namefromnode(node, &add_name);
|
|
|
|
|
|
|
|
dns_name_init(¤t_name, NULL);
|
|
|
|
|
|
|
|
do {
|
1999-01-25 15:46:30 +00:00
|
|
|
ancestors[depth++] = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
current = child;
|
|
|
|
|
|
|
|
dns_rbt_namefromnode(current, ¤t_name);
|
|
|
|
|
|
|
|
i = cmp_names_on_level(&add_name, ¤t_name);
|
|
|
|
if (i == 0)
|
1999-01-27 01:48:55 +00:00
|
|
|
return (DNS_R_EXISTS);
|
1999-01-20 10:13:43 +00:00
|
|
|
if (i < 0)
|
|
|
|
child = LEFT(current);
|
|
|
|
else
|
|
|
|
child = RIGHT(current);
|
|
|
|
} while (child != NULL);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
/* insist depth < space available */
|
|
|
|
ancestors[depth] = current;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (i < 0)
|
|
|
|
SET_LEFT(current, node);
|
|
|
|
else
|
|
|
|
SET_RIGHT(current, node);
|
|
|
|
MAKE_RED(node);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
while (node != root && IS_RED(ancestors[depth])) {
|
|
|
|
parent = ancestors[depth];
|
|
|
|
grandparent = ancestors[depth - 1];
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (parent == LEFT(grandparent)) {
|
|
|
|
child = RIGHT(grandparent);
|
|
|
|
if (child != NULL && IS_RED(child)) {
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
node = grandparent;
|
1999-01-25 15:46:30 +00:00
|
|
|
depth -= 2;
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
|
|
|
if (node == RIGHT(parent)) {
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_left(parent, grandparent, &root);
|
|
|
|
tmp = node;
|
1999-01-20 10:13:43 +00:00
|
|
|
node = parent;
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = tmp;
|
|
|
|
ancestors[depth] = parent;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_RED(grandparent);
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_right(grandparent, ancestors[depth - 2],
|
|
|
|
&root);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
child = LEFT(grandparent);
|
|
|
|
if (child != NULL && IS_RED(child)) {
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
node = grandparent;
|
1999-01-25 15:46:30 +00:00
|
|
|
depth -= 2;
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
|
|
|
if (node == LEFT(parent)) {
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_right(parent, grandparent, &root);
|
|
|
|
tmp = node;
|
1999-01-20 10:13:43 +00:00
|
|
|
node = parent;
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = tmp;
|
|
|
|
ancestors[depth] = parent;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_RED(grandparent);
|
1999-01-25 15:46:30 +00:00
|
|
|
rotate_left(grandparent, ancestors[depth - 2],
|
|
|
|
&root);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MAKE_BLACK(root);
|
|
|
|
*rootp = root;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
return (DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
static dns_result_t
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbt_delete_workhorse(dns_rbt_t *rbt, dns_rbtnode_t *delete) {
|
|
|
|
dns_rbtnode_t *sibling, *parent, *grandparent, *child;
|
|
|
|
dns_rbtnode_t *successor, **rootp;
|
|
|
|
int depth;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(delete);
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
if (rbt->level_count > 0)
|
|
|
|
rootp = &DOWN(rbt->levels[rbt->level_count - 1]);
|
|
|
|
else
|
|
|
|
rootp = &rbt->root;
|
|
|
|
|
|
|
|
child = NULL;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (LEFT(delete) == NULL)
|
|
|
|
if (RIGHT(delete) == NULL) {
|
1999-01-27 01:48:55 +00:00
|
|
|
if (rbt->ancestors[rbt->ancestor_count - 1] == NULL) {
|
|
|
|
/*
|
|
|
|
* This is the only item in the tree.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
*rootp = NULL;
|
1999-01-25 15:46:30 +00:00
|
|
|
return(DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
} else
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This node has one child, on the right.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
child = RIGHT(delete);
|
|
|
|
|
|
|
|
else if (RIGHT(delete) == NULL)
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This node has one child, on the left.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
child = LEFT(delete);
|
|
|
|
|
|
|
|
else {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t holder, *tmp = &holder;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
depth = rbt->ancestor_count++;
|
1999-01-20 10:13:43 +00:00
|
|
|
successor = RIGHT(delete);
|
1999-01-27 01:48:55 +00:00
|
|
|
while (LEFT(successor) != NULL) {
|
|
|
|
rbt->ancestors[rbt->ancestor_count++] = successor;
|
1999-01-20 10:13:43 +00:00
|
|
|
successor = LEFT(successor);
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The successor cannot possibly have a left child;
|
|
|
|
* if there is any child, it is on the right.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
if (RIGHT(successor))
|
|
|
|
child = RIGHT(successor);
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* First, put the successor in the tree location of the
|
|
|
|
* node to be deleted.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
memcpy(tmp, successor, sizeof(dns_rbtnode_t));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
rbt->ancestors[depth] = successor;
|
|
|
|
parent = rbt->ancestors[depth - 1];
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
if (LEFT(parent) == delete)
|
|
|
|
SET_LEFT(parent, successor);
|
|
|
|
else
|
|
|
|
SET_RIGHT(parent, successor);
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
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);
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
SET_LEFT(successor, LEFT(delete));
|
|
|
|
SET_RIGHT(successor, RIGHT(delete));
|
1999-01-27 01:48:55 +00:00
|
|
|
SET_COLOR(successor, COLOR(delete));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* Now relink the node to be deleted into the
|
|
|
|
* successor's previous tree location.
|
|
|
|
*/
|
|
|
|
parent = rbt->ancestors[rbt->ancestor_count - 1];
|
|
|
|
if (parent == successor)
|
|
|
|
SET_RIGHT(parent, delete);
|
|
|
|
else
|
|
|
|
SET_LEFT(parent, delete);
|
|
|
|
|
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
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);
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* Original successor node has no left.
|
|
|
|
*/
|
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
if (RIGHT(tmp) != NULL)
|
|
|
|
SET_PARENT(RIGHT(tmp), delete);
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
SET_LEFT(delete, NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
SET_RIGHT(delete, RIGHT(tmp));
|
1999-01-27 01:48:55 +00:00
|
|
|
SET_COLOR(delete, COLOR(tmp));
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
parent = rbt->ancestors[rbt->ancestor_count - 1];
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* Remove the node by removing the links from its parent.
|
|
|
|
*/
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == delete) {
|
|
|
|
SET_LEFT(parent, child);
|
|
|
|
sibling = RIGHT(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
1999-01-27 01:48:55 +00:00
|
|
|
SET_RIGHT(parent, child);
|
|
|
|
sibling = LEFT(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
if (child != NULL)
|
|
|
|
SET_PARENT(child, PARENT(delete));
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This is the root being deleted, and at this point
|
|
|
|
* it is known to have just one child.
|
|
|
|
*/
|
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
SET_PARENT(child, NULL);
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
|
|
|
sibling = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
*rootp = child;
|
|
|
|
}
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* Fix color violations.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
if (IS_BLACK(delete)) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t *parent;
|
1999-01-27 01:48:55 +00:00
|
|
|
depth = rbt->ancestor_count - 1;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
while (child != *rootp && IS_BLACK(child)) {
|
1999-01-27 01:48:55 +00:00
|
|
|
parent = rbt->ancestors[depth--];
|
|
|
|
grandparent = rbt->ancestors[depth];
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (LEFT(parent) == child) {
|
|
|
|
sibling = RIGHT(parent);
|
|
|
|
if (IS_RED(sibling)) {
|
|
|
|
MAKE_BLACK(sibling);
|
|
|
|
MAKE_RED(parent);
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_left(parent, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
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);
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_right(sibling, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
sibling = RIGHT(parent);
|
|
|
|
}
|
|
|
|
SET_COLOR(sibling, COLOR(parent));
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(RIGHT(sibling));
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_left(parent, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
child = *rootp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sibling = LEFT(parent);
|
|
|
|
if (IS_RED(sibling)) {
|
|
|
|
MAKE_BLACK(sibling);
|
|
|
|
MAKE_RED(parent);
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_right(parent, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
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);
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_left(sibling, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
sibling = LEFT(parent);
|
|
|
|
}
|
|
|
|
SET_COLOR(sibling, COLOR(parent));
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(LEFT(sibling));
|
1999-01-27 01:48:55 +00:00
|
|
|
rotate_right(parent, grandparent, rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
child = *rootp;
|
|
|
|
}
|
|
|
|
}
|
1999-01-27 01:48:55 +00:00
|
|
|
|
|
|
|
#if 0
|
1999-01-20 10:13:43 +00:00
|
|
|
parent = PARENT(child);
|
1999-01-27 01:48:55 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_RED(child))
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
return(DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-26 03:31:53 +00:00
|
|
|
/*
|
|
|
|
* This should only be used on the root of a tree, because no color fixup
|
|
|
|
* is done at all.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
static void
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_deletetree(isc_mem_t *mctx,
|
|
|
|
dns_rbtnode_t *node, dns_rbtnode_t **root) {
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-26 03:31:53 +00:00
|
|
|
if (node == NULL)
|
|
|
|
return;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (LEFT(node) != NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_deletetree(mctx, LEFT(node), root);
|
1999-01-20 10:13:43 +00:00
|
|
|
if (RIGHT(node) != NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_deletetree(mctx, RIGHT(node), root);
|
1999-01-20 10:13:43 +00:00
|
|
|
if (DOWN(node) != NULL)
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_deletetree(mctx, DOWN(node), &DOWN(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-26 03:31:53 +00:00
|
|
|
isc_mem_put(mctx, node, sizeof(*node) + NAMELEN(node));
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* @@@ is this necessary? only if the function is ever intended
|
|
|
|
* to be used to delete something where the root pointer needs to
|
|
|
|
* be told the tree is gone. At the moment, this is not the case,
|
|
|
|
* because the function is only used for two cases:
|
|
|
|
* + deleting everything DOWN from a node that is itself being deleted
|
|
|
|
* + deleting the entire tree of trees from dns_rbt_destroy.
|
|
|
|
* In each case, the root pointer is no longer relevant.
|
|
|
|
|
|
|
|
*/
|
1999-01-26 03:31:53 +00:00
|
|
|
*root = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
**
|
|
|
|
** Comparison functions.
|
|
|
|
**
|
|
|
|
**/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @@@ This is clearly too simplistic. I could use a dns_label_compare
|
|
|
|
* like dns_name_compare. Or perhaps i will just have to cast
|
|
|
|
* the labels into ad hoc dns_name_t structures and compare them.
|
|
|
|
* Note that it does absolutely no special comparison of bitstrings.
|
|
|
|
* This whole file as yet does nothing special with bitstrings.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmp_label(dns_label_t *a, dns_label_t *b) {
|
|
|
|
int i;
|
|
|
|
|
1999-01-30 04:27:13 +00:00
|
|
|
i = strncasecmp(a->base, b->base, MIN(a->length, b->length));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (i == 0 && a->length != b->length)
|
|
|
|
return(a->length < b->length ? -1 : 1);
|
|
|
|
else
|
|
|
|
return(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare a sequence of labels to determine if they are
|
|
|
|
* + FIRST_IS_LESS (a < b, and b.com < a.net)
|
|
|
|
* + FIRST_IS_MORE (a > b, but a.net > b.com)
|
|
|
|
* + BOTH_ARE_EQUAL (all labels in each)
|
|
|
|
* + in common (share suffixes: x.a.com and y.a.com have 2 labels in common;
|
|
|
|
* x.b.com and x.a.com have 1 label in common)
|
|
|
|
* If there are any common suffix labels, the return value is a natural
|
|
|
|
* number that indicates how many were in common.
|
|
|
|
*
|
|
|
|
* The root label is no included in the comparison, because it would not
|
|
|
|
* be helpful to compare two absolute names and have this function return
|
|
|
|
* that they had one element in common.
|
|
|
|
*
|
|
|
|
* @@@ As with cmp_label, this is too simplistic. Now lowercasing or
|
|
|
|
* bitstring comparisons are done.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
cmp_names_for_depth(dns_name_t *a, dns_name_t *b) {
|
|
|
|
dns_label_t alabel, blabel;
|
|
|
|
int aindex, bindex, compared, common;
|
|
|
|
|
|
|
|
aindex = dns_name_countlabels(a) - 1;
|
|
|
|
bindex = dns_name_countlabels(b) - 1;
|
|
|
|
|
|
|
|
INSIST(( dns_name_isabsolute(a) && dns_name_isabsolute(b)) ||
|
|
|
|
(!dns_name_isabsolute(a) && !dns_name_isabsolute(b)));
|
|
|
|
|
|
|
|
if (dns_name_isabsolute(a))
|
|
|
|
aindex--, bindex--;
|
|
|
|
|
|
|
|
common = 0;
|
|
|
|
|
|
|
|
for (; aindex >= 0 && bindex >= 0; aindex--, bindex--) {
|
|
|
|
dns_name_getlabel(a, aindex, &alabel);
|
|
|
|
dns_name_getlabel(b, bindex, &blabel);
|
|
|
|
|
|
|
|
compared = cmp_label(&alabel, &blabel);
|
|
|
|
if (compared == 0)
|
|
|
|
common++;
|
|
|
|
else if (common != 0)
|
|
|
|
return(common);
|
|
|
|
else if (compared < 0)
|
|
|
|
return(FIRST_IS_LESS);
|
|
|
|
else
|
|
|
|
return(FIRST_IS_MORE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aindex == -1 && bindex == -1)
|
|
|
|
return(BOTH_ARE_EQUAL);
|
|
|
|
else
|
|
|
|
return(common);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-01-26 03:31:53 +00:00
|
|
|
* This is meant only to be passed to RBT_INSERT by dns_rbt_addname.
|
1999-01-20 10:13:43 +00:00
|
|
|
* Since it is known it will not be called if there any suffixes
|
|
|
|
* in common, only the topmost label needs to be compared.
|
|
|
|
*
|
|
|
|
* @@@ As with cmp_label, this is too simplistic. Now lowercasing or
|
|
|
|
* bitstring comparisons are done.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
cmp_names_on_level(dns_name_t *a, dns_name_t *b) {
|
|
|
|
dns_label_t alabel, blabel;
|
|
|
|
int a_last_label, b_last_label;
|
|
|
|
|
|
|
|
a_last_label = dns_name_countlabels(a) - 1;
|
|
|
|
b_last_label = dns_name_countlabels(b) - 1;
|
|
|
|
|
|
|
|
INSIST(( dns_name_isabsolute(a) && dns_name_isabsolute(b)) ||
|
|
|
|
(!dns_name_isabsolute(a) && !dns_name_isabsolute(b)));
|
|
|
|
|
|
|
|
if (dns_name_isabsolute(a))
|
|
|
|
a_last_label--, b_last_label--;
|
|
|
|
|
|
|
|
dns_name_getlabel(a, a_last_label, &alabel);
|
|
|
|
dns_name_getlabel(b, b_last_label, &blabel);
|
|
|
|
|
|
|
|
return cmp_label(&alabel, &blabel);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isc_mem_t *mctx;
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
void
|
|
|
|
dns_rbt_indent(int depth) {
|
1999-01-20 10:13:43 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < depth; i++)
|
|
|
|
putchar('\t');
|
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
void
|
|
|
|
dns_rbt_printnodename(dns_rbtnode_t *node) {
|
1999-01-20 10:13:43 +00:00
|
|
|
char *buffer[255];
|
|
|
|
isc_buffer_t target;
|
|
|
|
isc_region_t r;
|
|
|
|
dns_name_t name;
|
|
|
|
|
|
|
|
r.length = NAMELEN(node);
|
|
|
|
r.base = NAME(node);
|
|
|
|
|
|
|
|
dns_name_init(&name, NULL);
|
|
|
|
dns_name_fromregion(&name, &r);
|
|
|
|
|
|
|
|
isc_buffer_init(&target, buffer, 255, ISC_BUFFERTYPE_TEXT);
|
|
|
|
dns_name_totext(&name, 1, &target);
|
|
|
|
|
|
|
|
printf("%.*s", (int)target.used, (char *)target.base);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
void
|
|
|
|
dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent, int depth) {
|
|
|
|
dns_rbt_indent(depth);
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (root != NULL) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printnodename(root);
|
1999-01-20 10:13:43 +00:00
|
|
|
printf(" (%s", IS_RED(root) ? "RED" : "black");
|
1999-01-25 15:46:30 +00:00
|
|
|
if (parent) {
|
1999-01-20 10:13:43 +00:00
|
|
|
printf(" from ");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printnodename(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
printf(")\n");
|
|
|
|
depth++;
|
|
|
|
|
|
|
|
if (DOWN(root)) {
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_indent(depth);
|
1999-01-20 10:13:43 +00:00
|
|
|
printf("++ BEG down from ");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printnodename(root);
|
1999-01-20 10:13:43 +00:00
|
|
|
printf("\n");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printtree(DOWN(root), NULL, depth);
|
|
|
|
dns_rbt_indent(depth);
|
1999-01-20 10:13:43 +00:00
|
|
|
printf("-- END down from ");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printnodename(root);
|
1999-01-20 10:13:43 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_RED(root) && IS_RED(LEFT(root)))
|
|
|
|
printf("** Red/Red color violation on left\n");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printtree(LEFT(root), root, depth);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (IS_RED(root) && IS_RED(RIGHT(root)))
|
|
|
|
printf("** Red/Red color violation on right\n");
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printtree(RIGHT(root), root, depth);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
} else
|
|
|
|
printf("NULL\n");
|
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
void
|
|
|
|
dns_rbt_printall(dns_rbt_t *rbt) {
|
|
|
|
dns_rbt_printtree(rbt->root, NULL, 0);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
/* DCL */
|