mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-30 22:15:20 +00:00
rbtnode struct adapted with more bitfields to represent color,
name attributes, name length, offset length, and number of bytes of "padding" that are left over when a node is split. adjusted node splitting algorithm to preserve the original location of the lower portion of the split node (eg, when a node has "a.b" as its name and is split into "a" and "b", "a" retains the same address space as "a.b".)
This commit is contained in:
@@ -32,7 +32,7 @@ ISC_LANG_BEGINDECLS
|
||||
* tree of trees. NOTE WELL: the implementation manages this as a variable
|
||||
* length structure, with the actual wire-format name and other data appended
|
||||
* appended to this structure. Allocating a contiguous block of memory for
|
||||
* multiple dns_rbt_node structures will not work.
|
||||
* multiple dns_rbtnode structures will not work.
|
||||
*/
|
||||
|
||||
typedef struct dns_rbt dns_rbt_t;
|
||||
@@ -46,20 +46,25 @@ typedef struct dns_rbtnode {
|
||||
struct dns_rbtnode *left;
|
||||
struct dns_rbtnode *right;
|
||||
struct dns_rbtnode *down;
|
||||
void *data;
|
||||
/*
|
||||
* We'd like to find a better place for the single bit of color
|
||||
* information. We can't pack it into the bitfield below, however,
|
||||
* because it's not covered by the node lock, and changing a single
|
||||
* bit in a bitfield is going to require a read-modify-write of a
|
||||
* word. This read-modify-write would include bits covered by a
|
||||
* lock we don't hold, and thus violate locking.
|
||||
* The following bitfields add up to a total bitwidth of 32.
|
||||
* The range of values necessary for each item is indicated,
|
||||
* but in the case of "attributes" the field is wider to accomodate
|
||||
* possible future expansion. "offsetlen" could be one bit
|
||||
* narrower by always adjusting its value by 1 to find the real
|
||||
* offsetlen, but doing so does not gain anything (except perhaps
|
||||
* another bit for "attributes", which doesn't yet need any more).
|
||||
*/
|
||||
unsigned int color;
|
||||
unsigned int color:1; /* range is 0..1 */
|
||||
unsigned int attributes:6; /* range is 0..2 */
|
||||
unsigned int namelen:8; /* range is 1..255 */
|
||||
unsigned int offsetlen:8; /* range is 1..128 */
|
||||
unsigned int padbytes:9; /* range is 0..380 */
|
||||
/*
|
||||
* These values are used in the RBT DB implementation. The appropriate
|
||||
* node lock must be held before accessing them.
|
||||
*/
|
||||
void *data;
|
||||
unsigned int dirty:1;
|
||||
unsigned int locknum:DNS_RBT_LOCKLENGTH;
|
||||
unsigned int references:DNS_RBT_REFLENGTH;
|
||||
|
187
lib/dns/rbt.c
187
lib/dns/rbt.c
@@ -57,24 +57,48 @@ struct dns_rbtnodechain {
|
||||
#define RED 0
|
||||
#define BLACK 1
|
||||
|
||||
/*
|
||||
* Elements of the rbtnode structure.
|
||||
*/
|
||||
#define LEFT(node) ((node)->left)
|
||||
#define RIGHT(node) ((node)->right)
|
||||
#define DOWN(node) ((node)->down)
|
||||
#define NAMELEN(node) (((unsigned char *)((node) + 1))[0])
|
||||
#define OFFSETLEN(node) (((unsigned char *)((node) + 1))[1])
|
||||
#define NAME(node) (&((unsigned char *)((node) + 1))[3])
|
||||
#define DATA(node) ((node)->data)
|
||||
#define COLOR(node) ((node)->color)
|
||||
#define NAMELEN(node) ((node)->namelen)
|
||||
#define OFFSETLEN(node) ((node)->offsetlen)
|
||||
#define ATTRS(node) ((node)->attributes)
|
||||
#define PADBYTES(node) ((node)->padbytes)
|
||||
|
||||
/*
|
||||
* Structure elements from the rbtdb.c, not
|
||||
* used as part of the rbt.c algorithms.
|
||||
*/
|
||||
#define DIRTY(node) ((node)->dirty)
|
||||
#define LOCK(node) ((node)->locknum)
|
||||
#define REFS(node) ((node)->references)
|
||||
|
||||
/*
|
||||
* The variable length stuff stored after the node.
|
||||
*/
|
||||
#define NAME(node) ((unsigned char *)((node) + 1))
|
||||
#define OFFSETS(node) (NAME(node) + NAMELEN(node))
|
||||
|
||||
#define NODE_SIZE(node) (sizeof(*node) + \
|
||||
NAMELEN(node) + OFFSETLEN(node) + PADBYTES(node))
|
||||
|
||||
/*
|
||||
* Color management.
|
||||
*/
|
||||
#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) + 3 + NAMELEN(node) + OFFSETLEN(node))
|
||||
#define ADD_ANCESTOR(chain, node) \
|
||||
(chain)->ancestors[(chain)->ancestor_count++] = (node)
|
||||
#define ADD_LEVEL(chain, node) \
|
||||
(chain)->levels[(chain)->level_count++] = (node)
|
||||
|
||||
/*
|
||||
* The following macros directly access normally private name variables.
|
||||
@@ -82,23 +106,14 @@ struct dns_rbtnodechain {
|
||||
* path of the tree traversal code.
|
||||
*/
|
||||
|
||||
#define ADD_ANCESTOR(chain, node) \
|
||||
(chain)->ancestors[(chain)->ancestor_count++] = (node)
|
||||
#define ADD_LEVEL(chain, node) \
|
||||
(chain)->levels[(chain)->level_count++] = (node)
|
||||
|
||||
#define NODENAME(node, name) \
|
||||
do { \
|
||||
unsigned char *__current; \
|
||||
(name)->attributes = DNS_NAMEATTR_READONLY; \
|
||||
__current = (unsigned char *)&(node)[1]; \
|
||||
(name)->length = *__current++; \
|
||||
(name)->labels = *__current++; \
|
||||
if (*__current++ == 1) \
|
||||
(name)->attributes |= DNS_NAMEATTR_ABSOLUTE; \
|
||||
(name)->ndata = __current; \
|
||||
__current += (name)->length; \
|
||||
(name)->offsets = __current; \
|
||||
(name)->length = NAMELEN(node); \
|
||||
(name)->labels = OFFSETLEN(node); \
|
||||
(name)->ndata = NAME(node); \
|
||||
(name)->offsets = OFFSETS(node); \
|
||||
(name)->attributes = ATTRS(node); \
|
||||
(name)->attributes |= DNS_NAMEATTR_READONLY; \
|
||||
} while (0)
|
||||
|
||||
#define FAST_ISABSOLUTE(name) \
|
||||
@@ -232,8 +247,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
/*
|
||||
* Does this thing have too many variables or what?
|
||||
*/
|
||||
dns_rbtnode_t **root, *current, *parent, *child;
|
||||
dns_rbtnode_t *new_node, *new_current;
|
||||
dns_rbtnode_t **root, *parent, *child, *current, *new_current;
|
||||
dns_name_t add_name, current_name, new_name, tmp_name;
|
||||
dns_offsets_t add_offsets, current_offsets, new_offsets, tmp_offsets;
|
||||
dns_namereln_t compared;
|
||||
@@ -257,10 +271,10 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
dns_name_fromregion(&add_name, &r);
|
||||
|
||||
if (rbt->root == NULL) {
|
||||
result = create_node(rbt->mctx, &add_name, &new_node);
|
||||
result = create_node(rbt->mctx, &add_name, &new_current);
|
||||
if (result == DNS_R_SUCCESS) {
|
||||
rbt->root = new_node;
|
||||
*nodep = new_node;
|
||||
rbt->root = new_current;
|
||||
*nodep = new_current;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
@@ -316,8 +330,6 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
}
|
||||
|
||||
} else {
|
||||
/* @@@ handle bitstrings */
|
||||
|
||||
/*
|
||||
* This name has some suffix in common with the
|
||||
* name at the current node. If the name at
|
||||
@@ -369,8 +381,22 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
* pointer made to a new tree.
|
||||
*/
|
||||
|
||||
INSIST(compared == dns_namereln_contains ||
|
||||
compared == dns_namereln_commonancestor);
|
||||
INSIST(compared == dns_namereln_commonancestor
|
||||
|| compared == dns_namereln_contains);
|
||||
|
||||
/* @@@ handle bitstrings.
|
||||
* When common_bits is non-zero, the last label
|
||||
* in common (eg, vix in a.vix.com vs
|
||||
* b.vix.com) is a bit label and common_bits is
|
||||
* how many are in common. To split the node,
|
||||
* the node in question will have to split into
|
||||
* two bitstrings. A comment in name.h says,
|
||||
* "Some provision still needs to be made for
|
||||
* splitting bitstring labels," and Bob has
|
||||
* pushed this down on the priority list,
|
||||
* so for now splitting on bitstrings does not
|
||||
* work.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get the common labels of the current name.
|
||||
@@ -414,10 +440,12 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
/*
|
||||
* 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()).
|
||||
* node, keeping the same memory location so
|
||||
* as not to break any external references to
|
||||
* the node. The down pointer and name data
|
||||
* are preserved, while left and right
|
||||
* pointers are nullified when the node is
|
||||
* established as the start of the next level.
|
||||
*/
|
||||
|
||||
start_label = 0;
|
||||
@@ -429,36 +457,37 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
keep_labels,
|
||||
&new_name);
|
||||
|
||||
|
||||
result = create_node(rbt->mctx,
|
||||
&new_name, &new_node);
|
||||
if (result != DNS_R_SUCCESS) {
|
||||
put_ancestor_mem(rbt->mctx, &chain);
|
||||
return (result);
|
||||
}
|
||||
|
||||
DATA(new_node) = DATA(current);
|
||||
DOWN(new_node) = DOWN(current);
|
||||
REFS(new_node) = REFS(current); /* @@@ ? */
|
||||
DIRTY(new_node) = DIRTY(current); /* @@@ ? */
|
||||
/* @@@ ? locknum */
|
||||
|
||||
/*
|
||||
* Now that the old name in the existing
|
||||
* node has been dissected into two new
|
||||
* nodes, the old node can be freed.
|
||||
* The name stored at the node is effectively
|
||||
* truncated in place by setting the shorter
|
||||
* name length, moving the offsets to the
|
||||
* end of the truncated name, and then
|
||||
* updating PADBYTES to reflect the truncation.
|
||||
*/
|
||||
isc_mem_put(rbt->mctx, current,
|
||||
NODE_SIZE(current));
|
||||
current = new_current;
|
||||
|
||||
NAMELEN(current) = new_name.length;
|
||||
OFFSETLEN(current) = keep_labels;
|
||||
memcpy(OFFSETS(current), new_name.offsets,
|
||||
keep_labels);
|
||||
PADBYTES(current) =
|
||||
(current_name.length - new_name.length)
|
||||
+ (current_labels - keep_labels);
|
||||
|
||||
/*
|
||||
* Set up the new root of the next level.
|
||||
* By definition it will not be the top
|
||||
* level tree, so clear DNS_NAMEATTR_ABSOLUTE.
|
||||
*/
|
||||
DOWN(current) = new_node;
|
||||
root = &DOWN(current);
|
||||
DOWN(new_current) = current;
|
||||
root = &DOWN(new_current);
|
||||
ADD_ANCESTOR(&chain, NULL);
|
||||
ADD_LEVEL(&chain, current);
|
||||
ADD_LEVEL(&chain, new_current);
|
||||
|
||||
LEFT(current) = NULL;
|
||||
RIGHT(current) = NULL;
|
||||
|
||||
MAKE_BLACK(current);
|
||||
ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
|
||||
|
||||
if (common_labels == add_labels) {
|
||||
/*
|
||||
@@ -466,7 +495,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
* the not-in-common parts down to
|
||||
* a new level.
|
||||
*/
|
||||
*nodep = current;
|
||||
*nodep = new_current;
|
||||
put_ancestor_mem(rbt->mctx, &chain);
|
||||
return (DNS_R_SUCCESS);
|
||||
|
||||
@@ -491,7 +520,6 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
keep_labels,
|
||||
&add_name);
|
||||
|
||||
current = new_node;
|
||||
child = NULL;
|
||||
ADD_ANCESTOR(&chain, current);
|
||||
}
|
||||
@@ -502,10 +530,10 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
|
||||
} while (child != NULL);
|
||||
|
||||
result = create_node(rbt->mctx, &add_name, &new_node);
|
||||
result = create_node(rbt->mctx, &add_name, &new_current);
|
||||
|
||||
if (result == DNS_R_SUCCESS)
|
||||
result = dns_rbt_addonlevel(new_node, current, order,
|
||||
result = dns_rbt_addonlevel(new_current, current, order,
|
||||
root, &chain);
|
||||
/* @@@ XXXRTH Free node if add fails? */
|
||||
/* @@@ XXXRTH Is it true that result should never be DNS_R_EXISTS? */
|
||||
@@ -514,7 +542,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
put_ancestor_mem(rbt->mctx, &chain);
|
||||
|
||||
if (result == DNS_R_SUCCESS)
|
||||
*nodep = new_node;
|
||||
*nodep = new_current;
|
||||
|
||||
return (result);
|
||||
}
|
||||
@@ -876,25 +904,19 @@ create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
dns_rbtnode_t *node;
|
||||
isc_region_t region;
|
||||
unsigned int labels;
|
||||
unsigned char *current;
|
||||
unsigned char absolute;
|
||||
|
||||
REQUIRE(name->offsets != NULL); /* @@@ XXX direct access to name. */
|
||||
|
||||
dns_name_toregion(name, ®ion);
|
||||
labels = FAST_COUNTLABELS(name);
|
||||
if (FAST_ISABSOLUTE(name))
|
||||
absolute = 1;
|
||||
else
|
||||
absolute = 0;
|
||||
ENSURE(labels > 0);
|
||||
|
||||
/*
|
||||
* Allocate space for the node structure, plus the length byte, the
|
||||
* offset length byte, the attributes byte (one byte for each is
|
||||
* 3 bytes), and plus the length of the name and the number of offsets.
|
||||
* Allocate space for the node structure, the namelen byte,
|
||||
* the namepad byte, the offsetlen/attrs byte, length of the name
|
||||
* and the number of offsets. @@@
|
||||
*/
|
||||
node = (dns_rbtnode_t *)isc_mem_get(mctx,
|
||||
sizeof(*node) + 3 +
|
||||
node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) +
|
||||
region.length + labels);
|
||||
|
||||
if (node == NULL)
|
||||
@@ -918,19 +940,18 @@ create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
|
||||
* 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.
|
||||
* This requires changes to lib/dns/name.c.
|
||||
* XXX The offsets table could be made smaller by eliminating the
|
||||
* first offset, which is always 0. This requires changes to
|
||||
* lib/dns/name.c.
|
||||
*/
|
||||
current = (unsigned char *)&node[1];
|
||||
*current++ = region.length;
|
||||
*current++ = labels;
|
||||
*current++ = absolute;
|
||||
memcpy(current, region.base, region.length);
|
||||
current += region.length;
|
||||
memcpy(current, name->offsets, labels);
|
||||
NAMELEN(node) = region.length;
|
||||
PADBYTES(node) = 0;
|
||||
OFFSETLEN(node) = labels;
|
||||
if (FAST_ISABSOLUTE(name))
|
||||
ATTRS(node) |= DNS_NAMEATTR_ABSOLUTE;
|
||||
|
||||
memcpy(NAME(node), region.base, region.length);
|
||||
memcpy(OFFSETS(node), name->offsets, labels);
|
||||
|
||||
*nodep = node;
|
||||
|
||||
|
Reference in New Issue
Block a user