rbt.c revision 1630fce031f7a3e33f0579e477a3e17d1993e1f9
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* 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.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <isc/assertions.h>
struct dns_rbt {
unsigned int magic;
void (*data_deleter)(void *, void *);
void * deleter_arg;
};
struct dns_rbtnodechain {
int ancestor_count;
int ancestor_maxitems;
/*
* 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.
*/
int level_count;
};
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#define RED 0
#define BLACK 1
/*
* The following macros directly access normally private name variables.
* These macros are used to avoid a lot of function calls in the critical
* path of the tree traversal code.
*/
do { \
unsigned char *__current; \
if (*__current++ == 1) \
} while (0)
do { \
else \
} while (0)
#define FAST_ISABSOLUTE(name) \
#define FAST_COUNTLABELS(name) \
/*
* For the return value of cmp_names_for_depth().
*/
#define BOTH_ARE_EQUAL 0
#define FIRST_IS_LESS -1
#define FIRST_IS_MORE -2
/*
* 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.
*/
#ifndef ISC_MEM_DEBUG
#define ANCESTOR_BLOCK 24
#else
#endif
#ifdef DEBUG
#define inline
/*
* A little something to help out in GDB.
*/
isc_region_t r;
return(r);
}
#endif
/*
* Forward declarations.
*/
dns_rbtnode_t **rootp);
dns_rbtnode_t **rootp);
dns_rbtnode_t **rootp);
/*
*/
{
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
/*
*/
void
#ifdef ISC_MEM_DEBUG
#endif
}
/*
* Add 'name' to tree, initializing its data pointer with 'data'.
*/
isc_region_t r;
/*
* Create a copy of the name so the original name structure is
* not modified.
*/
dns_name_toregion(name, &r);
dns_name_fromregion(&add_name, &r);
if (result == DNS_R_SUCCESS) {
}
return (result);
}
do {
if (compared == BOTH_ARE_EQUAL) {
return (DNS_R_EXISTS);
else
return (DNS_R_SUCCESS);
} else if (compared == FIRST_IS_LESS) {
} else if (compared == FIRST_IS_MORE) {
} else {
/*
* 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.
*/
/*
* 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.
*/
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;
&add_name);
/*
* Follow the down pointer (possibly NULL).
*/
} 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.
*/
&tmp_name);
&tmp_name, &new_current);
if (result != DNS_R_SUCCESS)
return (result);
/*
* Reproduce the tree attributes of the
* current node.
*/
/*
* Fix pointers that were to the current node.
*/
else
*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 (when
* the node is created in create_node()).
*/
start_label = 0;
&new_name);
if (result != DNS_R_SUCCESS)
return (result);
/* @@@ ? locknum */
/*
* Now that the old name in the existing
* node has been dissected into two new
* nodes, the old node can be freed.
*/
/*
* Set up the new root of the next level.
*/
if (compared == add_labels) {
/*
* The name has been added by pushing
* the not-in-common parts down to
* a new level.
*/
return (DNS_R_SUCCESS);
} else {
/*
* The current node has no data,
* because it is just a placeholder.
* It's data pointer is already NULL
* from create_node()).
*/
/* The not-in-common parts of the new
* name will be inserted into the new
* level following this loop.
*/
start_label = 0;
&add_name);
}
}
}
if (result == DNS_R_SUCCESS)
/* XXXRTH Free node if add fails? */
/* XXXRTH Is it true that result should never be DNS_R_EXISTS? */
if (chain.ancestor_maxitems > 0)
if (result == DNS_R_SUCCESS)
return (result);
}
if (result == DNS_R_SUCCESS)
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.
*/
isc_region_t r;
/*
* search_name is the name segment being sought in each tree level.
*/
dns_name_toregion(name, &r);
dns_name_fromregion(&orig, &r);
search_name = &orig;
current_name = &holder1;
chain->ancestor_maxitems = 0;
chain->ancestor_count = 0;
chain->level_count = 0;
return (NULL);
}
if (compared == BOTH_ARE_EQUAL)
break;
/*
* Expand the storage space for ancestors, if necessary.
*/
return (NULL);
/*
* Standard binary search tree movement.
*/
if (compared == FIRST_IS_LESS) {
} else if (compared == FIRST_IS_MORE) {
/*
* 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.
*/
if (compared == current_labels) {
/*
* Set up new name to search for as
* the not-in-common part.
*/
if (search_name == &holder2) {
current_name = &holder2;
} else {
current_name = &holder1;
}
- compared;
0,
/*
* Search in the next tree level, which
* won't be the top level tree anymore, so
* there is no root label to ignore.
*/
}
} else
/*
* Though there is a suffix in common, it
* isn't a down pointer, so the name does
* not exist.
*/
}
}
return (current);
}
void *
else
return(NULL);
}
/*
* Delete a name from the tree of trees.
*/
/*
* Find the node, building the ancestor chain.
*
* When searching, the name might not have an exact match:
* 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
* that the name to be deleted have data? For now, it is.
*
* @@@ how to ->dirty, ->locknum and ->references figure in?
*/
/*
* 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.
*/
if (chain.ancestor_maxitems > 0)
return (result);
}
static dns_result_t
if (chain->mem_failure)
return (DNS_R_NOMEMORY);
else
return (DNS_R_NOTFOUND);
if (recurse) {
} else {
rbt->deleter_arg);
/*
* 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.
*/
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.
*/
/*
* Everything is successful, unless the next block fails.
*/
/*
* 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.
*/
/*
* 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 ...
*
* 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.
*/
}
return (result);
}
void
}
static dns_result_t
unsigned int labels;
unsigned char *current;
unsigned char absolute;
if (FAST_ISABSOLUTE(name))
absolute = 1;
else
absolute = 0;
/*
* Allocate space for the node structure, plus the length byte,
* plus the length of the name.
*/
sizeof(*node) + 3 +
return (DNS_R_NOMEMORY);
/*
* To make reconstructing a name from the stored value in the node
* easy, we store the length of the name, the number of labels,
* whether the name is absolute or not, the name itself, and the
* name's offsets table.
*
* XXX Finding a way not to waste a byte on "absolute" would be
* a good thing, though it may be that we'll have to store
* other attributes someday. The offsets table could be made
* smaller by eliminating the first offset, which is always 0.
*/
return (DNS_R_SUCCESS);
}
static dns_result_t
isc_region_t r;
int newsize;
return (DNS_R_NOMEMORY);
dns_name_fromregion(&newname, &r);
if (result == DNS_R_SUCCESS) {
/*
* Fix the pointers to the original node.
*/
else
} else
}
return (result);
}
static inline dns_result_t
if (ancestor_mem == NULL) {
return (DNS_R_NOMEMORY);
}
if (oldsize > 0) {
}
return (DNS_R_SUCCESS);
}
static inline void
else
} else
}
static inline void
else
} else
}
/*
* This is the real workhorse of the insertion code, because it does the
*/
static dns_result_t
int i;
unsigned int depth;
chain->ancestor_maxitems = 0;
chain->ancestor_count = 0;
return (DNS_R_SUCCESS);
}
do {
return (DNS_R_NOMEMORY);
if (i == 0)
return (DNS_R_EXISTS);
if (i < 0)
else
return (DNS_R_NOMEMORY);
if (i < 0)
else
node = grandparent;
depth -= 2;
} else {
&root);
}
&root);
}
} else {
node = grandparent;
depth -= 2;
} else {
&root);
}
&root);
}
}
}
return (DNS_R_SUCCESS);
}
/*
* This is the real workhorse of the deletion code, because it does the
*
* The ancestor and level history _must_ be set with dns_rbt_findnode for
* this function to work properly.
*/
static void
int depth;
if (chain->level_count > 0)
else
/*
*/
== NULL) {
/*
* This is the only item in the tree.
*/
return;
}
} else
/*
* This node has one child, on the right.
*/
/*
* This node has one child, on the left.
*/
else {
/*
* 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.
*/
}
/*
* The successor cannot possibly have a left child;
* if there is any child, it is on the right.
*/
/* 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.
*/
/*
* First, put the successor in the tree location of the
* node to be deleted.
*/
if (parent)
else
else
/*
* Now relink the node to be deleted into the
* successor's previous tree location.
*/
else
/*
* Original location of successor node has no left.
*/
}
/*
* Remove the node by removing the links from its parent.
*/
} else {
}
} else {
/*
* This is the root being deleted, and at this point
* it is known to have just one child.
*/
}
/*
* Fix color violations.
*/
rootp);
}
} else {
rootp);
}
rootp);
}
} else {
rootp);
}
} else {
rootp);
}
rootp);
}
}
}
}
}
/*
* This should only be used on the root of a tree, because no color fixup
* is done at all.
*
* 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 accordingly.
*/
static void
return;
}
/**
**
** 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 inline int
int i;
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)
* 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
aabs = FAST_ISABSOLUTE(a);
babs = FAST_ISABSOLUTE(b);
if (aabs) {
aindex--;
bindex--;
}
common = 0;
if (compared == 0)
common++;
else if (common != 0)
return(common);
else if (compared < 0)
return(FIRST_IS_LESS);
else
return(FIRST_IS_MORE);
}
return(BOTH_ARE_EQUAL);
else
return(common);
}
/*
* This is meant only to be passed to RBT_INSERT by dns_rbt_addname.
* 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
int a_last_label, b_last_label;
aabs = FAST_ISABSOLUTE(a);
babs = FAST_ISABSOLUTE(b);
if (aabs) {
a_last_label--;
b_last_label--;
}
}
void
dns_rbt_indent(int depth) {
int i;
for (i = 0; i < depth; i++)
putchar('\t');
}
void
char *buffer[255];
isc_region_t r;
dns_name_fromregion(&name, &r);
}
void
if (parent) {
printf(" from ");
}
printf(")\n");
depth++;
printf("++ BEG down from ");
printf("\n");
printf("-- END down from ");
printf("\n");
}
} else
printf("NULL\n");
}
void
}
/* DCL */