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>
|
1999-01-31 18:43:57 +00:00
|
|
|
#include <isc/boolean.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
#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;
|
1999-01-31 00:52:53 +00:00
|
|
|
void (*data_deleter)(void *);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct node_chain {
|
|
|
|
dns_rbtnode_t ** ancestors;
|
|
|
|
int ancestor_count;
|
|
|
|
int ancestor_maxitems;
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* 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];
|
1999-01-31 00:52:53 +00:00
|
|
|
int level_count;
|
1999-01-31 18:43:57 +00:00
|
|
|
isc_boolean_t mem_failure;
|
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-31 00:52:53 +00:00
|
|
|
#define RED 0
|
|
|
|
#define BLACK 1
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
#define LEFT(node) ((node)->left)
|
|
|
|
#define RIGHT(node) ((node)->right)
|
|
|
|
#define DOWN(node) ((node)->down)
|
1999-01-31 00:52:53 +00:00
|
|
|
#define NAMELEN(node) (*(unsigned char *)((node) + 1))
|
|
|
|
#define NAME(node) ((void *)((char *)(node) + sizeof(*node) + 1))
|
1999-01-20 10:13:43 +00:00
|
|
|
#define DATA(node) ((node)->data)
|
|
|
|
#define COLOR(node) ((node)->color)
|
1999-01-31 00:52:53 +00:00
|
|
|
#define DIRTY(node) ((node)->dirty)
|
1999-01-31 01:35:04 +00:00
|
|
|
#define LOCK(node) ((node)->locknum)
|
1999-01-31 00:52:53 +00:00
|
|
|
#define REFS(node) ((node)->references)
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
#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)
|
|
|
|
|
|
|
|
#define NODE_SIZE(node) (sizeof(*node) + 1 + NAMELEN(node))
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For the return value of cmp_names_for_depth().
|
|
|
|
*/
|
|
|
|
#define BOTH_ARE_EQUAL 0
|
|
|
|
#define FIRST_IS_LESS -1
|
|
|
|
#define FIRST_IS_MORE -2
|
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
/*
|
|
|
|
* For use in allocating space for the chain of ancestor nodes.
|
|
|
|
*
|
|
|
|
* The maximum number of ancestors is theoretically not limited by the
|
|
|
|
* data tree. This initial value of 24 ancestors would be able to scan
|
|
|
|
* the full height of a single level of 16,777,216 nodes, more than double
|
|
|
|
* the current size of .com.
|
|
|
|
*/
|
|
|
|
#define ANCESTOR_BLOCK 24
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
#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
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
static dns_result_t join_nodes(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *node, dns_rbtnode_t *parent,
|
|
|
|
dns_rbtnode_t **rootp);
|
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
static inline dns_result_t get_ancestor_mem(isc_mem_t *mctx,
|
|
|
|
node_chain_t *chain);
|
|
|
|
|
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-31 00:52:53 +00:00
|
|
|
static dns_result_t dns_rbt_addonlevel(dns_rbtnode_t *node,
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbtnode_t **rootp);
|
1999-01-31 00:52:53 +00:00
|
|
|
static void dns_rbt_deletefromlevel(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *delete,
|
|
|
|
node_chain_t *chain);
|
1999-01-31 01:35:04 +00:00
|
|
|
static void dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
static dns_result_t zapnode_and_fixlevels(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *node,
|
|
|
|
isc_boolean_t recurse,
|
|
|
|
node_chain_t *chain);
|
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-31 00:52:53 +00:00
|
|
|
dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *), dns_rbt_t **rbtp) {
|
1999-01-20 10:13:43 +00:00
|
|
|
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;
|
1999-01-31 00:52:53 +00:00
|
|
|
rbt->data_deleter = deleter;
|
1999-01-20 10:13:43 +00:00
|
|
|
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-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(rbt, rbt->root);
|
1999-01-25 15:46:30 +00:00
|
|
|
|
|
|
|
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-31 00:52:53 +00:00
|
|
|
dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
|
|
|
dns_rbtnode_t **root, *current, *parent, *child;
|
|
|
|
dns_rbtnode_t *new_node, *new_current;
|
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
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-01-20 10:13:43 +00:00
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
1999-01-31 00:52:53 +00:00
|
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a copy of the name so the original name structure is
|
|
|
|
* not modified.
|
|
|
|
*/
|
|
|
|
memcpy(&add_name, name, sizeof(add_name));
|
|
|
|
|
|
|
|
if (rbt->root == NULL) {
|
|
|
|
result = create_node(rbt->mctx, &add_name, &new_node);
|
1999-01-31 00:52:53 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
rbt->root = new_node;
|
|
|
|
*nodep = 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);
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
if (compared == BOTH_ARE_EQUAL) {
|
|
|
|
*nodep = current;
|
1999-01-20 10:13:43 +00:00
|
|
|
if (DATA(current) != NULL)
|
1999-01-31 00:52:53 +00:00
|
|
|
return (DNS_R_EXISTS);
|
|
|
|
else
|
|
|
|
return (DNS_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
} else if (compared == FIRST_IS_LESS) {
|
1999-01-25 15:46:30 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
1999-01-31 00:52:53 +00:00
|
|
|
* Reproduce the tree attributes of the
|
|
|
|
* current node.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
LEFT(new_current) = LEFT(current);
|
|
|
|
RIGHT(new_current) = RIGHT(current);
|
|
|
|
COLOR(new_current) = COLOR(current);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix pointers that were to the current node.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
if (parent != NULL)
|
|
|
|
if (LEFT(parent) == current)
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = new_current;
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-01-31 01:35:04 +00:00
|
|
|
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
|
1999-01-31 00:52:53 +00:00
|
|
|
* and parent pointers are nullified (when
|
|
|
|
* the node is created in create_node()).
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
DATA(new_node) = DATA(current);
|
|
|
|
DOWN(new_node) = DOWN(current);
|
|
|
|
REFS(new_node) = REFS(current); /* @@@ ? */
|
|
|
|
DIRTY(new_node) = DIRTY(current); /* @@@ ? */
|
1999-01-31 01:35:04 +00:00
|
|
|
/* @@@ ? locknum */
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
1999-01-31 00:52:53 +00:00
|
|
|
NODE_SIZE(current));
|
1999-01-20 10:13:43 +00:00
|
|
|
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) {
|
|
|
|
/*
|
1999-01-31 00:52:53 +00:00
|
|
|
* The name has been added by pushing
|
|
|
|
* the not-in-common parts down to
|
|
|
|
* a new level.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
*nodep = current;
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
|
|
|
/*
|
1999-01-31 00:52:53 +00:00
|
|
|
* The current node has no data,
|
1999-01-20 10:13:43 +00:00
|
|
|
* because it is just a placeholder.
|
1999-01-31 00:52:53 +00:00
|
|
|
* It's data pointer is already NULL
|
|
|
|
* from create_node()).
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/* 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;
|
|
|
|
child = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (child != NULL);
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
result = create_node(rbt->mctx, &add_name, &new_node);
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS)
|
|
|
|
result = dns_rbt_addonlevel(new_node, root);
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS)
|
|
|
|
*nodep = new_node;
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_result_t
|
|
|
|
dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
|
|
|
|
dns_result_t result;
|
|
|
|
dns_rbtnode_t *node;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
|
|
|
node = NULL;
|
|
|
|
|
|
|
|
result = dns_rbt_addnode(rbt, name, &node);
|
|
|
|
if (result == DNS_R_SUCCESS)
|
|
|
|
DATA(node) = data;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
#define ADD_ANCESTOR(chain, node) \
|
|
|
|
(chain)->ancestors[(chain)->ancestor_count++] = (node)
|
|
|
|
#define ADD_LEVEL(chain, node) \
|
|
|
|
(chain)->levels[(chain)->level_count++] = (node)
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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-31 00:52:53 +00:00
|
|
|
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, node_chain_t *chain) {
|
1999-01-31 18:43:57 +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-31 00:52:53 +00:00
|
|
|
if (chain != NULL) {
|
1999-01-31 18:43:57 +00:00
|
|
|
chain->ancestor_maxitems = 0;
|
|
|
|
chain->ancestor_count = 0;
|
|
|
|
chain->level_count = 0;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
if (get_ancestor_mem(rbt->mctx, chain) != DNS_R_SUCCESS)
|
|
|
|
return (NULL);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
ADD_ANCESTOR(chain, NULL);
|
|
|
|
}
|
1999-01-31 18:43:57 +00:00
|
|
|
|
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;
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* Expand the storage space for ancestors, if necessary.
|
|
|
|
*/
|
|
|
|
if (chain != NULL &&
|
1999-01-31 18:43:57 +00:00
|
|
|
chain->ancestor_count == chain->ancestor_maxitems &&
|
|
|
|
get_ancestor_mem(rbt->mctx, chain) != DNS_R_SUCCESS)
|
|
|
|
return (NULL);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* Standard binary search tree movement.
|
|
|
|
*/
|
1999-01-27 01:48:55 +00:00
|
|
|
if (compared == FIRST_IS_LESS) {
|
1999-01-31 00:52:53 +00:00
|
|
|
if (chain != NULL)
|
|
|
|
ADD_ANCESTOR(chain, 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) {
|
1999-01-31 00:52:53 +00:00
|
|
|
if (chain != NULL)
|
|
|
|
ADD_ANCESTOR(chain, current);
|
1999-01-20 10:13:43 +00:00
|
|
|
current = RIGHT(current);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* The names have some common suffix labels.
|
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
} else {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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-31 00:52:53 +00:00
|
|
|
if (chain != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, NULL);
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
}
|
1999-01-27 01:48:55 +00:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
return (current);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
node = dns_rbt_findnode(rbt, name, NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (node != NULL && DATA(node) != NULL)
|
|
|
|
return(DATA(node));
|
|
|
|
else
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a name from the tree of trees.
|
|
|
|
*/
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
1999-01-31 18:43:57 +00:00
|
|
|
dns_rbtnode_t *node;
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_result_t result;
|
|
|
|
node_chain_t chain;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
/*
|
1999-01-27 01:48:55 +00:00
|
|
|
* Find the node, building the ancestor chain.
|
1999-01-31 00:52:53 +00:00
|
|
|
*
|
1999-01-31 18:43:57 +00:00
|
|
|
* When searching, the name might not have an exact match:
|
1999-01-31 00:52:53 +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
|
|
|
|
* a, b, and c. Deleting a.com would find only a partial depth
|
|
|
|
* match in the first layer. Should it be a requirement that
|
1999-01-31 18:43:57 +00:00
|
|
|
* that the name to be deleted have data? For now, it is.
|
1999-01-31 00:52:53 +00:00
|
|
|
*
|
1999-01-31 01:35:04 +00:00
|
|
|
* @@@ how to ->dirty, ->locknum and ->references figure in?
|
1999-01-25 15:46:30 +00:00
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
node = dns_rbt_findnode(rbt, name, &chain);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
/*
|
|
|
|
* The guts of this routine are in a separate function (which
|
|
|
|
* is called only once, by this function) to make freeing the
|
|
|
|
* ancestor memory easier, since there are several different
|
|
|
|
* exit points from the level checking logic.
|
|
|
|
*/
|
|
|
|
result = zapnode_and_fixlevels(rbt, node, recurse, &chain);
|
1999-01-31 16:50:01 +00:00
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
if (chain.ancestor_maxitems > 0)
|
1999-01-31 16:50:01 +00:00
|
|
|
isc_mem_put(rbt->mctx, chain.ancestors,
|
1999-01-31 18:43:57 +00:00
|
|
|
chain.ancestor_maxitems * sizeof(dns_rbtnode_t *));
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static dns_result_t
|
|
|
|
zapnode_and_fixlevels(dns_rbt_t *rbt, dns_rbtnode_t *node,
|
|
|
|
isc_boolean_t recurse, node_chain_t *chain) {
|
|
|
|
dns_rbtnode_t *down, *parent, **rootp;
|
|
|
|
dns_result_t result;
|
|
|
|
|
|
|
|
if (node == NULL || DATA(node) == NULL)
|
|
|
|
if (chain->mem_failure)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
else
|
|
|
|
return (DNS_R_NOTFOUND);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
down = DOWN(node);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
if (down != NULL) {
|
|
|
|
if (recurse) {
|
1999-01-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(rbt, down);
|
1999-01-31 00:52:53 +00:00
|
|
|
down = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
} else {
|
|
|
|
if (rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node));
|
|
|
|
DATA(node) = NULL;
|
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
if (LEFT(down) != NULL || RIGHT(down) != NULL)
|
1999-01-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* This node cannot be removed because it
|
|
|
|
* points down to a level that has more than
|
|
|
|
* one node, so it must continue to serve
|
|
|
|
* as the root for that level. All that
|
|
|
|
* could be done was to blast its data.
|
|
|
|
*/
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is a down pointer to a level with a single
|
|
|
|
* item. That item's name can be joined with the name
|
|
|
|
* on this level.
|
|
|
|
*/
|
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
rootp = chain->level_count > 0 ?
|
|
|
|
&DOWN(chain->levels[chain->level_count - 1]) :
|
1999-01-31 00:52:53 +00:00
|
|
|
&rbt->root;
|
1999-01-31 18:43:57 +00:00
|
|
|
parent = chain->ancestors[chain->ancestor_count - 1];
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
result = join_nodes(rbt, node, parent, rootp);
|
1999-01-31 16:50:01 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This node now has no down pointer (either because it didn't
|
|
|
|
* have one to start, or because it was recursively removed).
|
|
|
|
* So now the node needs to be removed from this level.
|
|
|
|
*/
|
1999-01-31 18:43:57 +00:00
|
|
|
dns_rbt_deletefromlevel(rbt, node, chain);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
if (rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node));
|
|
|
|
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Everything is successful, unless the next block fails.
|
|
|
|
*/
|
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is one node left on this level, and the node one level up
|
|
|
|
* that points down to here has no data, then those two nodes can be
|
|
|
|
* merged. The focus for exploring this criteria is shifted up one
|
|
|
|
* level.
|
|
|
|
*/
|
1999-01-31 18:43:57 +00:00
|
|
|
node = chain->level_count > 0 ?
|
|
|
|
chain->levels[chain->level_count - 1] : NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
if (node != NULL && DATA(node) == NULL &&
|
|
|
|
LEFT(DOWN(node)) == NULL && RIGHT(DOWN(node)) == NULL) {
|
1999-01-31 18:43:57 +00:00
|
|
|
rootp = chain->level_count > 1 ?
|
|
|
|
&DOWN(chain->levels[chain->level_count - 2]) :
|
1999-01-31 00:52:53 +00:00
|
|
|
&rbt->root;
|
|
|
|
/*
|
|
|
|
* The search to find the original node went through the
|
|
|
|
* node that is now being examined. It might have been
|
|
|
|
*
|
|
|
|
* current_node -down-to-> deleted_node ... or ...
|
|
|
|
*
|
|
|
|
* current_node -down-to-> remaining_node -left/right-to->
|
|
|
|
* deleted_node
|
|
|
|
*
|
|
|
|
* In the first case, ancestor_count - 1 is NULL and - 2
|
|
|
|
* is the parent of current_node (possibly also NULL).
|
|
|
|
* In the second case, ancestor_count - 1 is remaining_node,
|
|
|
|
* - 2, is NULL and - 3 is the parent of current_node.
|
|
|
|
*/
|
1999-01-31 18:43:57 +00:00
|
|
|
parent = chain->ancestors[chain->ancestor_count - 1] == NULL ?
|
|
|
|
chain->ancestors[chain->ancestor_count - 2] :
|
|
|
|
chain->ancestors[chain->ancestor_count - 3];
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
result = join_nodes(rbt, node, parent, rootp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
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-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* Allocate space for the node structure, plus the length byte,
|
|
|
|
* plus the length of the name.
|
|
|
|
*/
|
|
|
|
node = (dns_rbtnode_t *)isc_mem_get(mctx,
|
|
|
|
sizeof(*node) + 1 + 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
|
|
|
|
|
|
|
RIGHT(node) = NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
LEFT(node) = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
DOWN(node) = NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
DATA(node) = NULL;
|
|
|
|
|
1999-01-31 01:35:04 +00:00
|
|
|
LOCK(node) = 0; /* @@@ ? */
|
1999-01-31 00:52:53 +00:00
|
|
|
REFS(node) = 0;
|
|
|
|
DIRTY(node) = 0;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
static dns_result_t
|
|
|
|
join_nodes(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp) {
|
|
|
|
dns_rbtnode_t *down, *newnode;
|
|
|
|
dns_result_t result;
|
|
|
|
dns_name_t newname;
|
|
|
|
isc_region_t r;
|
|
|
|
int newsize;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(node != NULL);
|
|
|
|
REQUIRE(DATA(node) == NULL && DOWN(node) != NULL);
|
|
|
|
|
|
|
|
down = DOWN(node);
|
|
|
|
|
|
|
|
newsize = NAMELEN(node) + NAMELEN(down);
|
|
|
|
|
|
|
|
r.base = isc_mem_get(rbt->mctx, newsize);
|
|
|
|
if (r.base == NULL)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
|
|
|
|
memcpy(r.base,
|
|
|
|
NAME(down), NAMELEN(down));
|
|
|
|
memcpy(r.base + NAMELEN(down),
|
|
|
|
NAME(node), NAMELEN(node));
|
|
|
|
|
|
|
|
r.length = newsize;
|
|
|
|
|
|
|
|
dns_name_init(&newname, NULL);
|
|
|
|
dns_name_fromregion(&newname, &r);
|
|
|
|
|
|
|
|
result = create_node(rbt->mctx, &newname, &newnode);
|
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
isc_mem_put(rbt->mctx, r.base, r.length);
|
|
|
|
|
|
|
|
COLOR(newnode) = COLOR(node);
|
|
|
|
RIGHT(newnode) = RIGHT(node);
|
|
|
|
LEFT(newnode) = LEFT(node);
|
|
|
|
|
|
|
|
DOWN(newnode) = DOWN(down);
|
|
|
|
DATA(newnode) = DATA(down);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix the pointers to the original node.
|
|
|
|
*/
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
|
|
|
LEFT(parent) = newnode;
|
|
|
|
else
|
|
|
|
RIGHT(parent) = newnode;
|
|
|
|
|
|
|
|
} else
|
|
|
|
*rootp = newnode;
|
|
|
|
|
|
|
|
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
|
|
|
|
isc_mem_put(rbt->mctx, down, NODE_SIZE(down));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-01-31 18:43:57 +00:00
|
|
|
static inline dns_result_t
|
|
|
|
get_ancestor_mem(isc_mem_t *mctx, node_chain_t *chain) {
|
|
|
|
dns_rbtnode_t **ancestor_mem;
|
|
|
|
int oldsize, newsize;
|
|
|
|
|
|
|
|
oldsize = chain->ancestor_maxitems * sizeof(dns_rbtnode_t *);
|
|
|
|
newsize = oldsize + ANCESTOR_BLOCK * sizeof(dns_rbtnode_t *);
|
|
|
|
|
|
|
|
ancestor_mem = isc_mem_get(mctx, newsize);
|
|
|
|
|
|
|
|
if (ancestor_mem == NULL) {
|
|
|
|
chain->mem_failure = ISC_TRUE;
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->mem_failure = ISC_FALSE;
|
|
|
|
|
|
|
|
if (oldsize > 0) {
|
|
|
|
memcpy(ancestor_mem, chain->ancestors, oldsize);
|
|
|
|
isc_mem_put(mctx, chain->ancestors, oldsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->ancestors = ancestor_mem;
|
|
|
|
chain->ancestor_maxitems += ANCESTOR_BLOCK;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
1999-01-31 01:35:04 +00:00
|
|
|
RIGHT(node) = LEFT(child);
|
|
|
|
LEFT(child) = node;
|
1999-01-25 15:46:30 +00:00
|
|
|
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = child;
|
|
|
|
else
|
|
|
|
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);
|
|
|
|
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(node) = RIGHT(child);
|
|
|
|
RIGHT(child) = node;
|
1999-01-25 15:46:30 +00:00
|
|
|
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = child;
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-01-31 01:35:04 +00:00
|
|
|
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-31 00:52:53 +00:00
|
|
|
dns_rbt_addonlevel(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-31 00:52:53 +00:00
|
|
|
int i;
|
|
|
|
unsigned int 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-31 00:52:53 +00:00
|
|
|
INSIST(depth < sizeof(ancestors) / sizeof(*ancestors)); /* @@@ */
|
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-31 00:52:53 +00:00
|
|
|
INSIST(depth < sizeof(ancestors) / sizeof(*ancestors));/* @@@ */
|
1999-01-25 15:46:30 +00:00
|
|
|
ancestors[depth] = current;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (i < 0)
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(current) = node;
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-01-31 01:35:04 +00:00
|
|
|
RIGHT(current) = node;
|
1999-01-20 10:13:43 +00:00
|
|
|
MAKE_RED(node);
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
while (node != root && IS_RED(ancestors[depth])) {
|
1999-01-31 00:52:53 +00:00
|
|
|
INSIST(depth > 0);
|
1999-01-25 15:46:30 +00:00
|
|
|
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-31 00:52:53 +00:00
|
|
|
rotate_left(parent, grandparent,
|
|
|
|
&root);
|
1999-01-25 15:46:30 +00:00
|
|
|
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-31 00:52:53 +00:00
|
|
|
INSIST(depth > 1);
|
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-31 00:52:53 +00:00
|
|
|
rotate_right(parent, grandparent,
|
|
|
|
&root);
|
1999-01-25 15:46:30 +00:00
|
|
|
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-31 00:52:53 +00:00
|
|
|
INSIST(depth > 1);
|
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-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* This is the real workhorse of the deletion code, because it does the
|
|
|
|
* true red/black tree on a single level.
|
|
|
|
*
|
|
|
|
* The ancestor and level history _must_ be set with dns_rbt_findnode for
|
|
|
|
* this function to work properly.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dns_rbt_deletefromlevel(dns_rbt_t *rbt, dns_rbtnode_t *delete,
|
|
|
|
node_chain_t *chain) {
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbtnode_t *sibling, *parent, *grandparent, *child;
|
|
|
|
dns_rbtnode_t *successor, **rootp;
|
|
|
|
int depth;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-01-20 10:13:43 +00:00
|
|
|
REQUIRE(delete);
|
1999-01-31 18:43:57 +00:00
|
|
|
REQUIRE(chain->ancestor_count > 0);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
parent = chain->ancestors[chain->ancestor_count - 1];
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
if (chain->level_count > 0)
|
|
|
|
rootp = &DOWN(chain->levels[chain->level_count - 1]);
|
1999-01-27 01:48:55 +00:00
|
|
|
else
|
|
|
|
rootp = &rbt->root;
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* Verify that the ancestor/level history is (apparently) correct.
|
|
|
|
*/
|
|
|
|
REQUIRE((parent == NULL && *rootp == delete) ||
|
|
|
|
(parent != NULL &&
|
|
|
|
(LEFT(parent) == delete || RIGHT(parent) == delete)));
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
child = NULL;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
if (LEFT(delete) == NULL)
|
|
|
|
if (RIGHT(delete) == NULL) {
|
1999-01-31 00:52:53 +00:00
|
|
|
if (chain->ancestors[chain->ancestor_count - 1]
|
|
|
|
== NULL) {
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This is the only item in the tree.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
*rootp = NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
return;
|
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.
|
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
depth = chain->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) {
|
1999-01-31 00:52:53 +00:00
|
|
|
chain->ancestors[chain->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-31 00:52:53 +00:00
|
|
|
chain->ancestors[depth] = successor;
|
|
|
|
parent = chain->ancestors[depth - 1];
|
1999-01-27 01:48:55 +00:00
|
|
|
|
|
|
|
if (parent)
|
|
|
|
if (LEFT(parent) == delete)
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = successor;
|
1999-01-27 01:48:55 +00:00
|
|
|
else
|
1999-01-31 01:35:04 +00:00
|
|
|
RIGHT(parent) = successor;
|
1999-01-31 00:52:53 +00:00
|
|
|
else
|
|
|
|
*rootp = successor;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(successor) = LEFT(delete);
|
|
|
|
RIGHT(successor) = RIGHT(delete);
|
|
|
|
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.
|
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
parent = chain->ancestors[chain->ancestor_count - 1];
|
1999-01-27 01:48:55 +00:00
|
|
|
if (parent == successor)
|
1999-01-31 01:35:04 +00:00
|
|
|
RIGHT(parent) = delete;
|
1999-01-27 01:48:55 +00:00
|
|
|
else
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = delete;
|
1999-01-27 01:48:55 +00:00
|
|
|
|
|
|
|
/*
|
1999-01-31 00:52:53 +00:00
|
|
|
* Original location of successor node has no left.
|
1999-01-27 01:48:55 +00:00
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(delete) = NULL;
|
|
|
|
RIGHT(delete) = RIGHT(tmp);
|
|
|
|
COLOR(delete) = COLOR(tmp);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
parent = chain->ancestors[chain->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) {
|
1999-01-31 01:35:04 +00:00
|
|
|
LEFT(parent) = child;
|
1999-01-27 01:48:55 +00:00
|
|
|
sibling = RIGHT(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
1999-01-31 01:35:04 +00:00
|
|
|
RIGHT(parent) = child;
|
1999-01-27 01:48:55 +00:00
|
|
|
sibling = LEFT(parent);
|
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.
|
|
|
|
*/
|
|
|
|
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-31 00:52:53 +00:00
|
|
|
depth = chain->ancestor_count - 1;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
while (child != *rootp && IS_BLACK(child)) {
|
1999-01-31 00:52:53 +00:00
|
|
|
parent = chain->ancestors[depth--];
|
|
|
|
grandparent = chain->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-31 00:52:53 +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-31 00:52:53 +00:00
|
|
|
rotate_right(sibling,
|
|
|
|
grandparent,
|
|
|
|
rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
sibling = RIGHT(parent);
|
|
|
|
}
|
1999-01-31 01:35:04 +00:00
|
|
|
COLOR(sibling) = COLOR(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(RIGHT(sibling));
|
1999-01-31 00:52:53 +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-31 00:52:53 +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-31 00:52:53 +00:00
|
|
|
rotate_left(sibling,
|
|
|
|
grandparent,
|
|
|
|
rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
sibling = LEFT(parent);
|
|
|
|
}
|
1999-01-31 01:35:04 +00:00
|
|
|
COLOR(sibling) = COLOR(parent);
|
1999-01-20 10:13:43 +00:00
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(LEFT(sibling));
|
1999-01-31 00:52:53 +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
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_RED(child))
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-31 01:35:04 +00:00
|
|
|
*
|
|
|
|
* NOTE: No root pointer maintenance is done, 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, so there
|
|
|
|
* is no need for a root parameter to this function.
|
|
|
|
*
|
|
|
|
* If the function is ever intended to be used to delete something where
|
|
|
|
* a pointer needs to be told that this tree no longer exists,
|
|
|
|
* this function would need to adjusted accordinly.
|
1999-01-26 03:31:53 +00:00
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
static void
|
1999-01-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
|
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-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(rbt, LEFT(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
if (RIGHT(node) != NULL)
|
1999-01-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(rbt, RIGHT(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
if (DOWN(node) != NULL)
|
1999-01-31 01:35:04 +00:00
|
|
|
dns_rbt_deletetree(rbt, DOWN(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
if (DATA(node) != NULL && rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node));
|
|
|
|
|
|
|
|
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
|
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 */
|