mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-03 08:05:21 +00:00
added a function for getting space for ancestor nodes in the node_chain,
and use it within dns_rbt_findnode. moved the guts of dns_rbt_deletename into its own function to clean up the freeing of ancestor memory into just one location. deletefromlevel required that ancestor_count be > 1, which would abort trying to delete the root of the tree when the root had no children.
This commit is contained in:
166
lib/dns/rbt.c
166
lib/dns/rbt.c
@@ -22,6 +22,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <isc/assertions.h>
|
#include <isc/assertions.h>
|
||||||
|
#include <isc/boolean.h>
|
||||||
#include <isc/error.h>
|
#include <isc/error.h>
|
||||||
#include <isc/mem.h>
|
#include <isc/mem.h>
|
||||||
#include <isc/result.h>
|
#include <isc/result.h>
|
||||||
@@ -49,6 +50,7 @@ struct node_chain {
|
|||||||
*/
|
*/
|
||||||
dns_rbtnode_t * levels[127];
|
dns_rbtnode_t * levels[127];
|
||||||
int level_count;
|
int level_count;
|
||||||
|
isc_boolean_t mem_failure;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef MIN
|
#ifndef MIN
|
||||||
@@ -83,6 +85,16 @@ struct node_chain {
|
|||||||
#define FIRST_IS_LESS -1
|
#define FIRST_IS_LESS -1
|
||||||
#define FIRST_IS_MORE -2
|
#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.
|
||||||
|
*/
|
||||||
|
#define ANCESTOR_BLOCK 24
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define inline
|
#define inline
|
||||||
/*
|
/*
|
||||||
@@ -111,6 +123,9 @@ static dns_result_t join_nodes(dns_rbt_t *rbt,
|
|||||||
dns_rbtnode_t *node, dns_rbtnode_t *parent,
|
dns_rbtnode_t *node, dns_rbtnode_t *parent,
|
||||||
dns_rbtnode_t **rootp);
|
dns_rbtnode_t **rootp);
|
||||||
|
|
||||||
|
static inline dns_result_t get_ancestor_mem(isc_mem_t *mctx,
|
||||||
|
node_chain_t *chain);
|
||||||
|
|
||||||
static int cmp_label(dns_label_t *a, dns_label_t *b);
|
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_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);
|
static inline int cmp_names_for_depth(dns_name_t *a, dns_name_t *b);
|
||||||
@@ -127,6 +142,10 @@ static void dns_rbt_deletefromlevel(dns_rbt_t *rbt,
|
|||||||
node_chain_t *chain);
|
node_chain_t *chain);
|
||||||
static void dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
|
static void dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
|
||||||
|
|
||||||
|
static dns_result_t zapnode_and_fixlevels(dns_rbt_t *rbt,
|
||||||
|
dns_rbtnode_t *node,
|
||||||
|
isc_boolean_t recurse,
|
||||||
|
node_chain_t *chain);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize a red/black tree of trees.
|
* Initialize a red/black tree of trees.
|
||||||
@@ -453,7 +472,7 @@ dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
|
|||||||
*/
|
*/
|
||||||
dns_rbtnode_t *
|
dns_rbtnode_t *
|
||||||
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, node_chain_t *chain) {
|
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, node_chain_t *chain) {
|
||||||
dns_rbtnode_t *current, **ancestor_mem;
|
dns_rbtnode_t *current;
|
||||||
dns_name_t *search_name, *new_search_name, *current_name;
|
dns_name_t *search_name, *new_search_name, *current_name;
|
||||||
dns_name_t holder1, holder2;
|
dns_name_t holder1, holder2;
|
||||||
int compared, current_labels, keep_labels, dont_count_root_label;
|
int compared, current_labels, keep_labels, dont_count_root_label;
|
||||||
@@ -475,29 +494,16 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, node_chain_t *chain) {
|
|||||||
current_name = &holder1;
|
current_name = &holder1;
|
||||||
|
|
||||||
if (chain != NULL) {
|
if (chain != NULL) {
|
||||||
/*
|
chain->ancestor_maxitems = 0;
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
chain->ancestor_maxitems = 24;
|
|
||||||
ancestor_mem = isc_mem_get(rbt->mctx,
|
|
||||||
chain->ancestor_maxitems *
|
|
||||||
sizeof(dns_rbtnode_t *));
|
|
||||||
|
|
||||||
if (ancestor_mem == NULL)
|
|
||||||
return (NULL); /* @@@ flag no memory! best way? */
|
|
||||||
|
|
||||||
chain->ancestors = ancestor_mem;
|
|
||||||
|
|
||||||
chain->ancestor_count = 0;
|
chain->ancestor_count = 0;
|
||||||
ADD_ANCESTOR(chain, NULL);
|
|
||||||
|
|
||||||
chain->level_count = 0;
|
chain->level_count = 0;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (get_ancestor_mem(rbt->mctx, chain) != DNS_R_SUCCESS)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
ADD_ANCESTOR(chain, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
while (current != NULL) {
|
while (current != NULL) {
|
||||||
dns_rbt_namefromnode(current, current_name);
|
dns_rbt_namefromnode(current, current_name);
|
||||||
compared = cmp_names_for_depth(search_name, current_name);
|
compared = cmp_names_for_depth(search_name, current_name);
|
||||||
@@ -509,23 +515,9 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, node_chain_t *chain) {
|
|||||||
* Expand the storage space for ancestors, if necessary.
|
* Expand the storage space for ancestors, if necessary.
|
||||||
*/
|
*/
|
||||||
if (chain != NULL &&
|
if (chain != NULL &&
|
||||||
chain->ancestor_count == chain->ancestor_maxitems) {
|
chain->ancestor_count == chain->ancestor_maxitems &&
|
||||||
int oldsize = chain->ancestor_maxitems *
|
get_ancestor_mem(rbt->mctx, chain) != DNS_R_SUCCESS)
|
||||||
sizeof(dns_rbtnode_t *);
|
return (NULL);
|
||||||
|
|
||||||
chain->ancestor_maxitems += 24;
|
|
||||||
ancestor_mem = isc_mem_get(rbt->mctx,
|
|
||||||
chain->ancestor_maxitems *
|
|
||||||
sizeof(dns_rbtnode_t *));
|
|
||||||
|
|
||||||
if (ancestor_mem == NULL)
|
|
||||||
return (NULL); /* @@@ flag no mem! best way? */
|
|
||||||
|
|
||||||
memcpy(ancestor_mem, chain->ancestors, oldsize);
|
|
||||||
isc_mem_put(rbt->mctx, chain->ancestors, oldsize);
|
|
||||||
|
|
||||||
chain->ancestors = ancestor_mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Standard binary search tree movement.
|
* Standard binary search tree movement.
|
||||||
@@ -624,10 +616,9 @@ dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name) {
|
|||||||
*/
|
*/
|
||||||
dns_result_t
|
dns_result_t
|
||||||
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
||||||
dns_rbtnode_t *node, *down, *parent, **rootp;
|
dns_rbtnode_t *node;
|
||||||
dns_result_t result;
|
dns_result_t result;
|
||||||
node_chain_t chain;
|
node_chain_t chain;
|
||||||
int ancestor_memory_size;
|
|
||||||
|
|
||||||
REQUIRE(VALID_RBT(rbt));
|
REQUIRE(VALID_RBT(rbt));
|
||||||
REQUIRE(dns_name_isabsolute(name));
|
REQUIRE(dns_name_isabsolute(name));
|
||||||
@@ -635,27 +626,45 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
/*
|
/*
|
||||||
* Find the node, building the ancestor chain.
|
* Find the node, building the ancestor chain.
|
||||||
*
|
*
|
||||||
* @@@ When searching, the name might not have an exact match:
|
* When searching, the name might not have an exact match:
|
||||||
* consider a.b.a.com, b.b.a.com and c.b.a.com as the only
|
* 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
|
* 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
|
* 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
|
* a, b, and c. Deleting a.com would find only a partial depth
|
||||||
* match in the first layer. Should it be a requirement that
|
* match in the first layer. Should it be a requirement that
|
||||||
* that the name have been added with data? For now, it is.
|
* that the name to be deleted have data? For now, it is.
|
||||||
*
|
*
|
||||||
* @@@ how to ->dirty, ->locknum and ->references figure in?
|
* @@@ how to ->dirty, ->locknum and ->references figure in?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
node = dns_rbt_findnode(rbt, name, &chain);
|
node = dns_rbt_findnode(rbt, name, &chain);
|
||||||
|
|
||||||
ancestor_memory_size = chain.ancestor_maxitems *
|
/*
|
||||||
sizeof(dns_rbtnode_t *);
|
* 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);
|
||||||
|
|
||||||
if (node == NULL || DATA(node) == NULL) {
|
if (chain.ancestor_maxitems > 0)
|
||||||
isc_mem_put(rbt->mctx, chain.ancestors,
|
isc_mem_put(rbt->mctx, chain.ancestors,
|
||||||
ancestor_memory_size); /* @@@ */
|
chain.ancestor_maxitems * sizeof(dns_rbtnode_t *));
|
||||||
return (DNS_R_NOTFOUND);
|
|
||||||
}
|
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);
|
||||||
|
|
||||||
down = DOWN(node);
|
down = DOWN(node);
|
||||||
|
|
||||||
@@ -669,7 +678,7 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
rbt->data_deleter(DATA(node));
|
rbt->data_deleter(DATA(node));
|
||||||
DATA(node) = NULL;
|
DATA(node) = NULL;
|
||||||
|
|
||||||
if (LEFT(down) != NULL || RIGHT(down) != NULL) {
|
if (LEFT(down) != NULL || RIGHT(down) != NULL)
|
||||||
/*
|
/*
|
||||||
* This node cannot be removed because it
|
* This node cannot be removed because it
|
||||||
* points down to a level that has more than
|
* points down to a level that has more than
|
||||||
@@ -677,10 +686,7 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
* as the root for that level. All that
|
* as the root for that level. All that
|
||||||
* could be done was to blast its data.
|
* could be done was to blast its data.
|
||||||
*/
|
*/
|
||||||
isc_mem_put(rbt->mctx, chain.ancestors,
|
|
||||||
ancestor_memory_size); /* @@@ */
|
|
||||||
return (DNS_R_SUCCESS);
|
return (DNS_R_SUCCESS);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is a down pointer to a level with a single
|
* There is a down pointer to a level with a single
|
||||||
@@ -688,16 +694,13 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
* on this level.
|
* on this level.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rootp = chain.level_count > 0 ?
|
rootp = chain->level_count > 0 ?
|
||||||
&DOWN(chain.levels[chain.level_count - 1]) :
|
&DOWN(chain->levels[chain->level_count - 1]) :
|
||||||
&rbt->root;
|
&rbt->root;
|
||||||
parent = chain.ancestors[chain.ancestor_count - 1];
|
parent = chain->ancestors[chain->ancestor_count - 1];
|
||||||
|
|
||||||
result = join_nodes(rbt, node, parent, rootp);
|
result = join_nodes(rbt, node, parent, rootp);
|
||||||
|
|
||||||
isc_mem_put(rbt->mctx, chain.ancestors,
|
|
||||||
ancestor_memory_size); /* @@@ */
|
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,7 +710,7 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
* have one to start, or because it was recursively removed).
|
* have one to start, or because it was recursively removed).
|
||||||
* So now the node needs to be removed from this level.
|
* So now the node needs to be removed from this level.
|
||||||
*/
|
*/
|
||||||
dns_rbt_deletefromlevel(rbt, node, &chain);
|
dns_rbt_deletefromlevel(rbt, node, chain);
|
||||||
|
|
||||||
if (rbt->data_deleter != NULL)
|
if (rbt->data_deleter != NULL)
|
||||||
rbt->data_deleter(DATA(node));
|
rbt->data_deleter(DATA(node));
|
||||||
@@ -724,13 +727,13 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
* merged. The focus for exploring this criteria is shifted up one
|
* merged. The focus for exploring this criteria is shifted up one
|
||||||
* level.
|
* level.
|
||||||
*/
|
*/
|
||||||
node = chain.level_count > 0 ?
|
node = chain->level_count > 0 ?
|
||||||
chain.levels[chain.level_count - 1] : NULL;
|
chain->levels[chain->level_count - 1] : NULL;
|
||||||
|
|
||||||
if (node != NULL && DATA(node) == NULL &&
|
if (node != NULL && DATA(node) == NULL &&
|
||||||
LEFT(DOWN(node)) == NULL && RIGHT(DOWN(node)) == NULL) {
|
LEFT(DOWN(node)) == NULL && RIGHT(DOWN(node)) == NULL) {
|
||||||
rootp = chain.level_count > 1 ?
|
rootp = chain->level_count > 1 ?
|
||||||
&DOWN(chain.levels[chain.level_count - 2]) :
|
&DOWN(chain->levels[chain->level_count - 2]) :
|
||||||
&rbt->root;
|
&rbt->root;
|
||||||
/*
|
/*
|
||||||
* The search to find the original node went through the
|
* The search to find the original node went through the
|
||||||
@@ -746,14 +749,13 @@ dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
|||||||
* In the second case, ancestor_count - 1 is remaining_node,
|
* In the second case, ancestor_count - 1 is remaining_node,
|
||||||
* - 2, is NULL and - 3 is the parent of current_node.
|
* - 2, is NULL and - 3 is the parent of current_node.
|
||||||
*/
|
*/
|
||||||
parent = chain.ancestors[chain.ancestor_count - 1] == NULL ?
|
parent = chain->ancestors[chain->ancestor_count - 1] == NULL ?
|
||||||
chain.ancestors[chain.ancestor_count - 2] :
|
chain->ancestors[chain->ancestor_count - 2] :
|
||||||
chain.ancestors[chain.ancestor_count - 3];
|
chain->ancestors[chain->ancestor_count - 3];
|
||||||
|
|
||||||
result = join_nodes(rbt, node, parent, rootp);
|
result = join_nodes(rbt, node, parent, rootp);
|
||||||
}
|
}
|
||||||
|
|
||||||
isc_mem_put(rbt->mctx, chain.ancestors, ancestor_memory_size);/* @@@ */
|
|
||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,6 +865,34 @@ join_nodes(dns_rbt_t *rbt,
|
|||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp) {
|
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp) {
|
||||||
dns_rbtnode_t *child;
|
dns_rbtnode_t *child;
|
||||||
@@ -1040,7 +1070,7 @@ dns_rbt_deletefromlevel(dns_rbt_t *rbt, dns_rbtnode_t *delete,
|
|||||||
|
|
||||||
REQUIRE(VALID_RBT(rbt));
|
REQUIRE(VALID_RBT(rbt));
|
||||||
REQUIRE(delete);
|
REQUIRE(delete);
|
||||||
REQUIRE(chain->ancestor_count > 1);
|
REQUIRE(chain->ancestor_count > 0);
|
||||||
|
|
||||||
parent = chain->ancestors[chain->ancestor_count - 1];
|
parent = chain->ancestors[chain->ancestor_count - 1];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user