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-04-09 22:55:20 +00:00
|
|
|
/* $Id: rbt.c,v 1.36 1999/04/09 22:55:20 tale Exp $ */
|
1999-03-18 21:21:31 +00:00
|
|
|
|
|
|
|
/* Principal Authors: DCL */
|
|
|
|
|
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-03-16 16:10:36 +00:00
|
|
|
#include <dns/fixedname.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
#define RBT_MAGIC 0x5242542BU /* RBT+. */
|
|
|
|
#define VALID_RBT(rbt) ((rbt) != NULL && (rbt)->magic == RBT_MAGIC)
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
#define CHAIN_MAGIC 0x302d302dU /* 0-0-. */
|
|
|
|
#define VALID_CHAIN(chain) ((chain) != NULL && \
|
|
|
|
(chain)->magic == CHAIN_MAGIC)
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
struct dns_rbt {
|
|
|
|
unsigned int magic;
|
|
|
|
isc_mem_t * mctx;
|
1999-01-27 01:48:55 +00:00
|
|
|
dns_rbtnode_t * root;
|
1999-02-06 01:27:35 +00:00
|
|
|
void (*data_deleter)(void *, void *);
|
|
|
|
void * deleter_arg;
|
1999-01-31 00:52:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define RED 0
|
|
|
|
#define BLACK 1
|
|
|
|
|
1999-03-11 18:54:31 +00:00
|
|
|
/*
|
|
|
|
* Elements of the rbtnode structure.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
#define LEFT(node) ((node)->left)
|
|
|
|
#define RIGHT(node) ((node)->right)
|
|
|
|
#define DOWN(node) ((node)->down)
|
|
|
|
#define DATA(node) ((node)->data)
|
|
|
|
#define COLOR(node) ((node)->color)
|
1999-04-01 15:57:48 +00:00
|
|
|
#define CALLBACK(node) ((node)->find_callback)
|
1999-03-11 18:54:31 +00:00
|
|
|
#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.
|
|
|
|
*/
|
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-03-11 18:54:31 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
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)
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
/*
|
|
|
|
* Chain management.
|
|
|
|
*/
|
1999-03-11 18:54:31 +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-02-06 01:27:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define NODENAME(node, name) \
|
|
|
|
do { \
|
1999-03-11 18:54:31 +00:00
|
|
|
(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; \
|
1999-02-06 01:27:35 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define FAST_ISABSOLUTE(name) \
|
|
|
|
(((name)->attributes & DNS_NAMEATTR_ABSOLUTE) ? ISC_TRUE : ISC_FALSE)
|
|
|
|
|
|
|
|
#define FAST_COUNTLABELS(name) \
|
|
|
|
((name)->labels)
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define inline
|
|
|
|
/*
|
|
|
|
* A little something to help out in GDB.
|
|
|
|
*/
|
1999-03-12 05:00:32 +00:00
|
|
|
dns_name_t Name(dns_rbtnode_t *node);
|
|
|
|
dns_name_t
|
1999-01-25 15:46:30 +00:00
|
|
|
Name(dns_rbtnode_t *node) {
|
1999-03-12 05:00:32 +00:00
|
|
|
dns_name_t name;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_name_init(&name, NULL);
|
|
|
|
if (node != NULL)
|
1999-03-30 01:56:01 +00:00
|
|
|
NODENAME(node, &name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
return (name);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
#if defined(ISC_MEM_DEBUG) || defined(RBT_MEM_TEST)
|
|
|
|
#undef DNS_RBT_ANCESTORBLOCK
|
|
|
|
#define DNS_RBT_ANCESTORBLOCK 1 /* To give the reallocation code a workout. */
|
|
|
|
#endif
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
static inline dns_result_t get_ancestor_mem(dns_rbtnodechain_t *chain);
|
|
|
|
static inline void put_ancestor_mem(dns_rbtnodechain_t *chain);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
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-03-12 05:00:32 +00:00
|
|
|
static void dns_rbt_addonlevel(dns_rbtnode_t *node,
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_rbtnode_t *current, int order,
|
1999-02-01 03:26:00 +00:00
|
|
|
dns_rbtnode_t **rootp,
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_t *chain);
|
1999-01-31 00:52:53 +00:00
|
|
|
static void dns_rbt_deletefromlevel(dns_rbt_t *rbt,
|
|
|
|
dns_rbtnode_t *delete,
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_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,
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_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-03-12 05:00:32 +00:00
|
|
|
dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
|
|
|
|
void *deleter_arg, dns_rbt_t **rbtp)
|
1999-02-06 01:27:35 +00:00
|
|
|
{
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_rbt_t *rbt;
|
|
|
|
|
|
|
|
REQUIRE(mctx != NULL);
|
|
|
|
REQUIRE(rbtp != NULL && *rbtp == NULL);
|
1999-03-12 05:00:32 +00:00
|
|
|
REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
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-03-12 05:00:32 +00:00
|
|
|
rbt->deleter_arg = deleter_arg;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-03-12 05:00:32 +00:00
|
|
|
* Deallocate a red/black tree of trees.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
|
|
|
static inline dns_result_t
|
|
|
|
get_ancestor_mem(dns_rbtnodechain_t *chain) {
|
|
|
|
dns_rbtnode_t **ancestor_mem;
|
|
|
|
int oldsize, newsize;
|
|
|
|
|
|
|
|
oldsize = chain->ancestor_maxitems * sizeof(dns_rbtnode_t *);
|
|
|
|
newsize = oldsize + DNS_RBT_ANCESTORBLOCK * sizeof(dns_rbtnode_t *);
|
|
|
|
|
|
|
|
if (oldsize == 0) {
|
|
|
|
chain->ancestors = chain->ancestor_block;
|
|
|
|
} else {
|
|
|
|
ancestor_mem = isc_mem_get(chain->mctx, newsize);
|
|
|
|
|
|
|
|
if (ancestor_mem == NULL)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
|
|
|
|
memcpy(ancestor_mem, chain->ancestors, oldsize);
|
|
|
|
|
|
|
|
if (chain->ancestor_maxitems > DNS_RBT_ANCESTORBLOCK)
|
|
|
|
isc_mem_put(chain->mctx, chain->ancestors, oldsize);
|
|
|
|
|
|
|
|
chain->ancestors = ancestor_mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->ancestor_maxitems += DNS_RBT_ANCESTORBLOCK;
|
|
|
|
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
/*
|
|
|
|
* This is used by functions that are popping the chain off their
|
|
|
|
* own stack, and so do not need to have ancestor_maxitems or the
|
|
|
|
* ancestors pointer reset. Functions that will be reusing a chain
|
|
|
|
* structure need to call dns_rbtnodechain_reset() instead.
|
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
static inline void
|
|
|
|
put_ancestor_mem(dns_rbtnodechain_t *chain) {
|
|
|
|
if (chain->ancestor_maxitems > DNS_RBT_ANCESTORBLOCK)
|
|
|
|
isc_mem_put(chain->mctx, chain->ancestors,
|
|
|
|
chain->ancestor_maxitems
|
|
|
|
* sizeof(dns_rbtnode_t *));
|
|
|
|
}
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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) {
|
1999-03-03 20:01:49 +00:00
|
|
|
/*
|
|
|
|
* Does this thing have too many variables or what?
|
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
dns_rbtnode_t **root, *parent, *child, *current, *new_current;
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_t add_name, current_name, new_name, tmp_name;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_offsets_t add_offsets, current_offsets, new_offsets, tmp_offsets;
|
|
|
|
dns_namereln_t compared;
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_result_t result;
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_t chain;
|
|
|
|
isc_region_t r;
|
1999-03-04 21:03:29 +00:00
|
|
|
unsigned int add_labels, current_labels, keep_labels, start_label;
|
1999-03-04 02:35:04 +00:00
|
|
|
unsigned int common_labels, common_bits;
|
1999-03-04 21:03:29 +00:00
|
|
|
int order;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-02-06 01:27:35 +00:00
|
|
|
REQUIRE(FAST_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.
|
|
|
|
*/
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(&add_name, add_offsets);
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_name_toregion(name, &r);
|
|
|
|
dns_name_fromregion(&add_name, &r);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (rbt->root == NULL) {
|
1999-03-11 18:54:31 +00:00
|
|
|
result = create_node(rbt->mctx, &add_name, &new_current);
|
1999-01-31 00:52:53 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
1999-03-11 18:54:31 +00:00
|
|
|
rbt->root = new_current;
|
|
|
|
*nodep = new_current;
|
1999-01-31 00:52:53 +00:00
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
chain.ancestor_maxitems = 0;
|
|
|
|
chain.ancestor_count = 0;
|
|
|
|
chain.level_count = 0;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
chain.mctx = rbt->mctx;
|
|
|
|
if (get_ancestor_mem(&chain) != DNS_R_SUCCESS)
|
1999-03-03 20:01:49 +00:00
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
ADD_ANCESTOR(&chain, NULL);
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
root = &rbt->root;
|
1999-01-25 15:46:30 +00:00
|
|
|
parent = NULL;
|
1999-03-03 20:01:49 +00:00
|
|
|
current = NULL;
|
1999-01-20 10:13:43 +00:00
|
|
|
child = *root;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(¤t_name, current_offsets);
|
1999-01-20 10:13:43 +00:00
|
|
|
do {
|
|
|
|
current = child;
|
|
|
|
|
1999-02-06 01:27:35 +00:00
|
|
|
NODENAME(current, ¤t_name);
|
1999-03-03 20:01:49 +00:00
|
|
|
compared = dns_name_fullcompare(&add_name, ¤t_name,
|
|
|
|
&order,
|
|
|
|
&common_labels, &common_bits);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (compared == dns_namereln_equal) {
|
1999-01-31 00:52:53 +00:00
|
|
|
*nodep = current;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
put_ancestor_mem(&chain);
|
1999-03-12 05:00:32 +00:00
|
|
|
return (DNS_R_EXISTS);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Expand the storage space for ancestors, if necessary.
|
|
|
|
*/
|
|
|
|
if (chain.ancestor_count == chain.ancestor_maxitems &&
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
get_ancestor_mem(&chain) != DNS_R_SUCCESS) {
|
|
|
|
put_ancestor_mem(&chain);
|
1999-03-12 05:00:32 +00:00
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
}
|
1999-03-03 20:01:49 +00:00
|
|
|
|
|
|
|
if (compared == dns_namereln_none) {
|
|
|
|
if (order < 0) {
|
|
|
|
parent = current;
|
|
|
|
child = LEFT(current);
|
|
|
|
ADD_ANCESTOR(&chain, current);
|
|
|
|
|
|
|
|
} else if (order > 0) {
|
|
|
|
parent = current;
|
|
|
|
child = RIGHT(current);
|
|
|
|
ADD_ANCESTOR(&chain, current);
|
|
|
|
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
add_labels = FAST_COUNTLABELS(&add_name);
|
|
|
|
current_labels = FAST_COUNTLABELS(¤t_name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (compared == dns_namereln_subdomain) {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
keep_labels = add_labels - common_labels;
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
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-03-03 20:01:49 +00:00
|
|
|
ADD_ANCESTOR(&chain, NULL);
|
|
|
|
ADD_LEVEL(&chain, 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.
|
|
|
|
*/
|
1999-03-03 20:01:49 +00:00
|
|
|
|
1999-03-11 18:54:31 +00:00
|
|
|
INSIST(compared == dns_namereln_commonancestor
|
|
|
|
|| compared == dns_namereln_contains);
|
|
|
|
|
1999-03-30 01:56:01 +00:00
|
|
|
/*
|
|
|
|
* Ensure the number of levels in the tree
|
|
|
|
* does not exceed the number of logical
|
|
|
|
* levels allowed by DNSSEC.
|
|
|
|
*
|
|
|
|
* XXX DCL need a better error result?
|
|
|
|
*/
|
|
|
|
if (chain.level_count ==
|
|
|
|
(sizeof(chain.levels) /
|
|
|
|
sizeof(*chain.levels)))
|
|
|
|
return (DNS_R_NOSPACE);
|
|
|
|
|
1999-03-18 20:30:28 +00:00
|
|
|
/* XXX DCL handle bitstrings.
|
1999-03-11 18:54:31 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
1999-03-03 20:01:49 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
1999-03-03 20:01:49 +00:00
|
|
|
* Get the common labels of the current name.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
start_label = current_labels - common_labels;
|
|
|
|
keep_labels = common_labels;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(&tmp_name, tmp_offsets);
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_getlabelsequence(¤t_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&tmp_name);
|
|
|
|
|
|
|
|
result = create_node(rbt->mctx,
|
|
|
|
&tmp_name, &new_current);
|
1999-03-03 20:01:49 +00:00
|
|
|
if (result != DNS_R_SUCCESS) {
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
put_ancestor_mem(&chain);
|
1999-01-20 10:13:43 +00:00
|
|
|
return (result);
|
1999-03-03 20:01:49 +00:00
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
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
|
1999-03-11 18:54:31 +00:00
|
|
|
* 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.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
start_label = 0;
|
1999-03-03 20:01:49 +00:00
|
|
|
keep_labels = current_labels - common_labels;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(&new_name, new_offsets);
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_getlabelsequence(¤t_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&new_name);
|
|
|
|
|
|
|
|
/*
|
1999-03-11 18:54:31 +00:00
|
|
|
* 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.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
|
|
|
|
NAMELEN(current) = new_name.length;
|
|
|
|
OFFSETLEN(current) = keep_labels;
|
|
|
|
memcpy(OFFSETS(current), new_name.offsets,
|
|
|
|
keep_labels);
|
1999-03-11 20:07:49 +00:00
|
|
|
PADBYTES(current) +=
|
1999-03-11 18:54:31 +00:00
|
|
|
(current_name.length - new_name.length)
|
|
|
|
+ (current_labels - keep_labels);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
/*
|
1999-01-25 15:46:30 +00:00
|
|
|
* Set up the new root of the next level.
|
1999-03-11 18:54:31 +00:00
|
|
|
* By definition it will not be the top
|
|
|
|
* level tree, so clear DNS_NAMEATTR_ABSOLUTE.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
DOWN(new_current) = current;
|
|
|
|
root = &DOWN(new_current);
|
1999-03-03 20:01:49 +00:00
|
|
|
ADD_ANCESTOR(&chain, NULL);
|
1999-03-11 18:54:31 +00:00
|
|
|
ADD_LEVEL(&chain, new_current);
|
|
|
|
|
|
|
|
LEFT(current) = NULL;
|
|
|
|
RIGHT(current) = NULL;
|
|
|
|
|
|
|
|
MAKE_BLACK(current);
|
|
|
|
ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (common_labels == add_labels) {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
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-03-11 18:54:31 +00:00
|
|
|
*nodep = new_current;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
put_ancestor_mem(&chain);
|
1999-01-31 00:52:53 +00:00
|
|
|
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-03-03 20:01:49 +00:00
|
|
|
* Its data pointer is already NULL
|
1999-01-31 00:52:53 +00:00
|
|
|
* 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;
|
1999-03-03 20:01:49 +00:00
|
|
|
keep_labels =
|
|
|
|
add_labels - common_labels;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
dns_name_getlabelsequence(&add_name,
|
|
|
|
start_label,
|
|
|
|
keep_labels,
|
|
|
|
&add_name);
|
|
|
|
|
|
|
|
child = NULL;
|
1999-03-03 20:01:49 +00:00
|
|
|
ADD_ANCESTOR(&chain, current);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (child != NULL);
|
|
|
|
|
1999-03-11 18:54:31 +00:00
|
|
|
result = create_node(rbt->mctx, &add_name, &new_current);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
dns_rbt_addonlevel(new_current, current, order, root, &chain);
|
|
|
|
*nodep = new_current;
|
|
|
|
}
|
1999-02-01 03:26:00 +00:00
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
put_ancestor_mem(&chain);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
/*
|
|
|
|
* Add a name to the tree of trees, associating it with some data.
|
|
|
|
*/
|
1999-01-31 00:52:53 +00:00
|
|
|
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));
|
1999-02-06 01:27:35 +00:00
|
|
|
REQUIRE(FAST_ISABSOLUTE(name));
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
node = NULL;
|
|
|
|
|
|
|
|
result = dns_rbt_addnode(rbt, name, &node);
|
1999-03-12 05:00:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* dns_rbt_addnode will report the node exists even when
|
|
|
|
* it does not have data associated with it, but the
|
|
|
|
* dns_rbt_*name functions all behave depending on whether
|
|
|
|
* there is data associated with a node.
|
|
|
|
*/
|
|
|
|
if (result == DNS_R_SUCCESS ||
|
|
|
|
(result == DNS_R_EXISTS && DATA(node) == NULL)) {
|
1999-01-31 00:52:53 +00:00
|
|
|
DATA(node) = data;
|
1999-03-12 05:00:32 +00:00
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the node for "name" in the tree of trees.
|
|
|
|
*/
|
1999-03-04 21:03:29 +00:00
|
|
|
dns_result_t
|
1999-03-16 16:10:36 +00:00
|
|
|
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
|
|
|
|
isc_boolean_t empty_data_ok, dns_rbtfindcallback_t callback,
|
|
|
|
void *callback_arg)
|
1999-03-04 21:03:29 +00:00
|
|
|
{
|
1999-01-31 18:43:57 +00:00
|
|
|
dns_rbtnode_t *current;
|
1999-03-16 16:10:36 +00:00
|
|
|
dns_name_t *search_name, *current_name, *tmp_name;
|
|
|
|
dns_name_t name1, name2, *current_foundname, *new_foundname;
|
|
|
|
dns_fixedname_t foundname1, foundname2;
|
|
|
|
dns_offsets_t name1_offsets, name2_offsets;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_namereln_t compared;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
dns_result_t result, saved_result;
|
1999-02-06 01:27:35 +00:00
|
|
|
isc_region_t r;
|
1999-03-16 16:10:36 +00:00
|
|
|
unsigned int current_labels, common_labels, common_bits;
|
|
|
|
unsigned int first_common_label, foundname_labels;
|
1999-03-04 21:03:29 +00:00
|
|
|
int order;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-02-06 01:27:35 +00:00
|
|
|
REQUIRE(FAST_ISABSOLUTE(name));
|
1999-03-04 21:03:29 +00:00
|
|
|
REQUIRE(node != NULL && *node == NULL);
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a chain it needs to appear to be in a sane state.
|
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
REQUIRE(chain == NULL ||
|
1999-04-09 15:21:15 +00:00
|
|
|
(VALID_CHAIN(chain) && chain->end == NULL &&
|
|
|
|
chain->ancestor_count == 0 && chain->level_count == 0));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
dns_name_init(&name1, name1_offsets);
|
|
|
|
dns_name_init(&name2, name2_offsets);
|
1999-03-03 20:01:49 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* search_name is the name segment being sought in each tree level.
|
1999-03-03 20:01:49 +00:00
|
|
|
* Ensure that it has offsets by making a copy into a structure
|
|
|
|
* that has offsets.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-03-16 16:10:36 +00:00
|
|
|
if (name->offsets == NULL) {
|
|
|
|
search_name = &name1;
|
|
|
|
dns_name_toregion(name, &r);
|
|
|
|
dns_name_fromregion(search_name, &r);
|
|
|
|
} else
|
|
|
|
search_name = name;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
current_name = &name2;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
/*
|
|
|
|
* Initialize the building of the name of the returned node.
|
|
|
|
* This is not wrapped in "if (foundname != NULL)" because gcc
|
|
|
|
* gives spurious warnings otherwise.
|
|
|
|
*/
|
|
|
|
dns_fixedname_init(&foundname1);
|
|
|
|
dns_fixedname_init(&foundname2);
|
|
|
|
current_foundname = dns_fixedname_name(&foundname1);
|
|
|
|
new_foundname = dns_fixedname_name(&foundname2);
|
|
|
|
foundname_labels = 0;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
/*
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
* If chaining, initialize the chain.
|
1999-03-16 16:10:36 +00:00
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
if (chain != NULL)
|
1999-01-31 00:52:53 +00:00
|
|
|
ADD_ANCESTOR(chain, NULL);
|
1999-03-16 16:10:36 +00:00
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
saved_result = DNS_R_SUCCESS;
|
1999-03-16 16:10:36 +00:00
|
|
|
current = rbt->root;
|
1999-01-20 10:13:43 +00:00
|
|
|
while (current != NULL) {
|
1999-02-06 01:27:35 +00:00
|
|
|
NODENAME(current, current_name);
|
1999-03-03 20:01:49 +00:00
|
|
|
compared = dns_name_fullcompare(search_name, current_name,
|
|
|
|
&order,
|
|
|
|
&common_labels, &common_bits);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (compared == dns_namereln_equal)
|
1999-01-20 10:13:43 +00:00
|
|
|
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 &&
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
get_ancestor_mem(chain) != DNS_R_SUCCESS)
|
1999-03-12 05:00:32 +00:00
|
|
|
return (DNS_R_NOMEMORY);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (compared == dns_namereln_none) {
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
/*
|
|
|
|
* Standard binary search tree movement.
|
|
|
|
*/
|
1999-03-03 20:01:49 +00:00
|
|
|
if (order < 0) {
|
|
|
|
if (chain != NULL)
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = LEFT(current);
|
|
|
|
} else if (order > 0) {
|
|
|
|
if (chain != NULL)
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = RIGHT(current);
|
|
|
|
}
|
1999-01-31 00:52:53 +00:00
|
|
|
} else {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
* The names have some common suffix labels.
|
|
|
|
*
|
1999-03-03 20:01:49 +00:00
|
|
|
* If the number in common are equal in length to
|
|
|
|
* the current node's name length, then follow the
|
|
|
|
* down pointer and search in the new tree.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-03-03 20:01:49 +00:00
|
|
|
current_labels = FAST_COUNTLABELS(current_name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (common_labels == current_labels) {
|
1999-03-16 16:10:36 +00:00
|
|
|
/*
|
1999-01-20 10:13:43 +00:00
|
|
|
* Set up new name to search for as
|
1999-03-16 16:10:36 +00:00
|
|
|
* the not-in-common part, and build foundname.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
1999-03-16 16:10:36 +00:00
|
|
|
if (search_name == &name2) {
|
|
|
|
current_name = &name2;
|
|
|
|
tmp_name = &name1;
|
|
|
|
dns_name_init(tmp_name, name1_offsets);
|
|
|
|
|
|
|
|
if (foundname != NULL) {
|
1999-03-16 22:57:32 +00:00
|
|
|
current_foundname =
|
|
|
|
dns_fixedname_name(&foundname1);
|
|
|
|
new_foundname =
|
|
|
|
dns_fixedname_name(&foundname2);
|
|
|
|
dns_fixedname_init(&foundname2);
|
1999-03-16 16:10:36 +00:00
|
|
|
}
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
} else {
|
1999-03-16 16:10:36 +00:00
|
|
|
current_name = &name1;
|
|
|
|
tmp_name = &name2;
|
|
|
|
dns_name_init(tmp_name, name2_offsets);
|
|
|
|
|
|
|
|
if (foundname != NULL) {
|
1999-03-16 22:57:32 +00:00
|
|
|
current_foundname =
|
|
|
|
dns_fixedname_name(&foundname2);
|
|
|
|
new_foundname =
|
|
|
|
dns_fixedname_name(&foundname1);
|
|
|
|
dns_fixedname_init(&foundname1);
|
1999-03-16 16:10:36 +00:00
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
first_common_label =
|
|
|
|
FAST_COUNTLABELS(search_name)
|
1999-03-03 20:01:49 +00:00
|
|
|
- common_labels;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
if (foundname != NULL) {
|
|
|
|
/*
|
1999-03-16 22:57:32 +00:00
|
|
|
* Build foundname by getting the
|
|
|
|
* common labels and prefixing them
|
|
|
|
* to the current foundname.
|
1999-03-16 16:10:36 +00:00
|
|
|
*/
|
|
|
|
dns_name_getlabelsequence(search_name,
|
1999-03-16 22:57:32 +00:00
|
|
|
first_common_label,
|
|
|
|
current_labels,
|
|
|
|
tmp_name);
|
1999-03-16 16:10:36 +00:00
|
|
|
|
|
|
|
result = dns_name_concatenate(tmp_name,
|
1999-03-16 22:57:32 +00:00
|
|
|
current_foundname,
|
|
|
|
new_foundname,
|
|
|
|
NULL);
|
1999-03-16 16:10:36 +00:00
|
|
|
if (result != DNS_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-03-16 22:57:32 +00:00
|
|
|
* Whack off the current node's common labels
|
|
|
|
* for the name to search in the next level.
|
1999-03-16 16:10:36 +00:00
|
|
|
*/
|
|
|
|
dns_name_getlabelsequence(search_name, 0,
|
|
|
|
first_common_label,
|
|
|
|
tmp_name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
search_name = tmp_name;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
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-03-04 21:03:29 +00:00
|
|
|
/*
|
|
|
|
* This might be the closest enclosing name.
|
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
if (empty_data_ok || DATA(current) != NULL) {
|
1999-03-12 05:00:32 +00:00
|
|
|
*node = current;
|
1999-03-16 16:10:36 +00:00
|
|
|
foundname_labels =
|
1999-03-16 22:57:32 +00:00
|
|
|
FAST_COUNTLABELS(new_foundname);
|
1999-03-16 16:10:36 +00:00
|
|
|
}
|
1999-03-04 21:03:29 +00:00
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
/*
|
|
|
|
* The caller may want to interrupt the downward
|
|
|
|
* search when certain special nodes are
|
|
|
|
* traversed. If this is a special node, the
|
|
|
|
* callback is used to learn what the caller
|
|
|
|
* wants to do.
|
|
|
|
*/
|
|
|
|
if (callback != NULL && CALLBACK(current)) {
|
|
|
|
result = (callback)(current,
|
|
|
|
new_foundname,
|
|
|
|
callback_arg);
|
|
|
|
if (result != DNS_R_CONTINUE) {
|
|
|
|
saved_result = result;
|
|
|
|
/*
|
|
|
|
* Treat this node as if it
|
|
|
|
* had no down pointer.
|
|
|
|
*/
|
|
|
|
current = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
/*
|
|
|
|
* Search in the next tree level.
|
|
|
|
*/
|
1999-01-20 10:13:43 +00:00
|
|
|
current = DOWN(current);
|
|
|
|
|
1999-03-04 21:03:29 +00:00
|
|
|
} else {
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* Though there is a suffix in common, it
|
1999-03-03 20:01:49 +00:00
|
|
|
* has no down pointer, so the name does
|
1999-01-20 10:13:43 +00:00
|
|
|
* not exist.
|
|
|
|
*/
|
|
|
|
current = NULL;
|
1999-03-04 21:03:29 +00:00
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-03-04 21:03:29 +00:00
|
|
|
if (current != NULL) {
|
1999-04-09 15:21:15 +00:00
|
|
|
if (chain != NULL)
|
|
|
|
chain->end = current;
|
|
|
|
|
1999-03-16 16:10:36 +00:00
|
|
|
if (foundname != NULL)
|
|
|
|
result = dns_name_concatenate(current_name,
|
|
|
|
new_foundname, foundname,
|
|
|
|
NULL);
|
|
|
|
else
|
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
*node = current;
|
|
|
|
result = saved_result;
|
|
|
|
} else
|
|
|
|
*node = NULL;
|
1999-03-16 16:10:36 +00:00
|
|
|
|
|
|
|
} else if (*node != NULL) {
|
|
|
|
if (foundname != NULL) {
|
|
|
|
current_labels = FAST_COUNTLABELS(new_foundname);
|
|
|
|
|
|
|
|
if (current_labels != foundname_labels) {
|
|
|
|
dns_name_init(&name1, name1_offsets);
|
|
|
|
dns_name_getlabelsequence(new_foundname,
|
|
|
|
current_labels -
|
|
|
|
foundname_labels,
|
|
|
|
foundname_labels,
|
|
|
|
&name1);
|
|
|
|
result = dns_name_concatenate(&name1, NULL,
|
|
|
|
foundname, NULL);
|
|
|
|
} else
|
|
|
|
result = dns_name_concatenate(new_foundname,
|
|
|
|
NULL,
|
|
|
|
foundname, NULL);
|
|
|
|
} else
|
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS)
|
|
|
|
result = DNS_R_PARTIALMATCH;
|
|
|
|
|
|
|
|
} else
|
1999-03-04 21:03:29 +00:00
|
|
|
result = DNS_R_NOTFOUND;
|
|
|
|
|
|
|
|
return (result);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
/*
|
|
|
|
* Get the data pointer associated with 'name'.
|
|
|
|
*/
|
1999-03-04 21:03:29 +00:00
|
|
|
dns_result_t
|
1999-03-16 16:10:36 +00:00
|
|
|
dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name,
|
|
|
|
dns_name_t *foundname, void **data) {
|
1999-03-04 21:03:29 +00:00
|
|
|
dns_rbtnode_t *node = NULL;
|
|
|
|
dns_result_t result;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-04 21:03:29 +00:00
|
|
|
REQUIRE(data != NULL && *data == NULL);
|
1999-01-27 01:48:55 +00:00
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,
|
|
|
|
ISC_FALSE, NULL, NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
if (node != NULL && DATA(node) != NULL)
|
1999-03-04 21:03:29 +00:00
|
|
|
*data = DATA(node);
|
1999-01-20 10:13:43 +00:00
|
|
|
else
|
1999-03-04 21:03:29 +00:00
|
|
|
result = DNS_R_NOTFOUND;
|
|
|
|
|
|
|
|
return (result);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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-03-04 21:03:29 +00:00
|
|
|
dns_rbtnode_t *node = NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_result_t result;
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_t chain;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-02-06 01:27:35 +00:00
|
|
|
REQUIRE(FAST_ISABSOLUTE(name));
|
1999-01-31 00:52:53 +00:00
|
|
|
|
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-03-30 01:56:01 +00:00
|
|
|
* ->dirty, ->locknum and ->references are ignored; they are
|
|
|
|
* solely the province of rbtdb.c.
|
1999-01-25 15:46:30 +00:00
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
dns_rbtnodechain_init(&chain, rbt->mctx);
|
|
|
|
result = dns_rbt_findnode(rbt, name, NULL, &node, &chain, ISC_FALSE,
|
|
|
|
NULL, NULL);
|
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.
|
|
|
|
*/
|
1999-03-12 05:00:32 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
if (DATA(node) != NULL)
|
|
|
|
result = zapnode_and_fixlevels(rbt, node,
|
|
|
|
recurse, &chain);
|
|
|
|
else
|
|
|
|
result = DNS_R_NOTFOUND;
|
1999-01-31 16:50:01 +00:00
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
} else if (result == DNS_R_PARTIALMATCH)
|
|
|
|
result = DNS_R_NOTFOUND;
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
put_ancestor_mem(&chain);
|
1999-01-31 18:43:57 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +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,
|
1999-02-06 01:27:35 +00:00
|
|
|
isc_boolean_t recurse, dns_rbtnodechain_t *chain) {
|
1999-01-31 18:43:57 +00:00
|
|
|
dns_rbtnode_t *down, *parent, **rootp;
|
|
|
|
dns_result_t result;
|
|
|
|
|
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)
|
1999-02-06 01:27:35 +00:00
|
|
|
rbt->data_deleter(DATA(node),
|
|
|
|
rbt->deleter_arg);
|
1999-01-31 00:52:53 +00:00
|
|
|
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)
|
1999-02-06 01:27:35 +00:00
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
1999-01-31 00:52:53 +00:00
|
|
|
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
|
|
|
|
1999-02-06 01:27:35 +00:00
|
|
|
REQUIRE(name->offsets == NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-02-06 01:27:35 +00:00
|
|
|
NODENAME(node, name);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
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;
|
1999-02-06 01:27:35 +00:00
|
|
|
unsigned int labels;
|
|
|
|
|
1999-03-30 01:56:01 +00:00
|
|
|
REQUIRE(name->offsets != NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
dns_name_toregion(name, ®ion);
|
1999-02-06 01:27:35 +00:00
|
|
|
labels = FAST_COUNTLABELS(name);
|
1999-03-11 18:54:31 +00:00
|
|
|
ENSURE(labels > 0);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
/*
|
1999-03-18 20:30:28 +00:00
|
|
|
* Allocate space for the node structure, the name, and the offsets.
|
1999-01-31 00:52:53 +00:00
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) +
|
1999-02-06 01:27:35 +00:00
|
|
|
region.length + labels);
|
|
|
|
|
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-02-06 01:27:35 +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);
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
CALLBACK(node) = 0;
|
1999-01-25 15:46:30 +00:00
|
|
|
|
1999-02-06 01:27:35 +00:00
|
|
|
/*
|
1999-03-03 20:01:49 +00:00
|
|
|
* The following is stored to make reconstructing a name from the
|
|
|
|
* stored value in the node easy: 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.
|
1999-02-06 01:27:35 +00:00
|
|
|
*
|
1999-03-30 01:56:01 +00:00
|
|
|
* XXX RTH
|
1999-03-18 20:30:28 +00:00
|
|
|
* The offsets table could be made smaller by eliminating the
|
1999-03-11 18:54:31 +00:00
|
|
|
* first offset, which is always 0. This requires changes to
|
|
|
|
* lib/dns/name.c.
|
1999-02-06 01:27:35 +00:00
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
NAMELEN(node) = region.length;
|
|
|
|
PADBYTES(node) = 0;
|
|
|
|
OFFSETLEN(node) = labels;
|
1999-03-30 01:56:01 +00:00
|
|
|
ATTRS(node) = name->attributes;
|
1999-03-11 18:54:31 +00:00
|
|
|
|
|
|
|
memcpy(NAME(node), region.base, region.length);
|
|
|
|
memcpy(OFFSETS(node), name->offsets, labels);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
*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;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_offsets_t offsets;
|
1999-01-31 00:52:53 +00:00
|
|
|
isc_region_t r;
|
1999-03-11 22:04:24 +00:00
|
|
|
int newlabels, newsize;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(node != NULL);
|
|
|
|
REQUIRE(DATA(node) == NULL && DOWN(node) != NULL);
|
|
|
|
|
|
|
|
down = DOWN(node);
|
|
|
|
|
|
|
|
newsize = NAMELEN(node) + NAMELEN(down);
|
1999-03-11 22:04:24 +00:00
|
|
|
newlabels = OFFSETLEN(node) + OFFSETLEN(down);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
r.base = isc_mem_get(rbt->mctx, newsize);
|
|
|
|
if (r.base == NULL)
|
|
|
|
return (DNS_R_NOMEMORY);
|
|
|
|
|
1999-03-11 22:04:24 +00:00
|
|
|
memcpy(r.base, NAME(down), NAMELEN(down));
|
|
|
|
memcpy(r.base + NAMELEN(down), NAME(node), NAMELEN(node));
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
r.length = newsize;
|
|
|
|
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_name_init(&newname, offsets);
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_name_fromregion(&newname, &r);
|
|
|
|
|
1999-03-11 22:04:24 +00:00
|
|
|
/*
|
|
|
|
* Check whether the space needed for the joined names can
|
|
|
|
* fit within the space already available in the down node,
|
|
|
|
* so that any external references to the down node are preserved.
|
|
|
|
*
|
|
|
|
* Currently this is not very meaningful since preservation
|
|
|
|
* of the address of the down node cannot be guaranteed.
|
|
|
|
*/
|
|
|
|
if (newsize + newlabels >=
|
|
|
|
NAMELEN(down) + OFFSETLEN(down) + PADBYTES(down))
|
|
|
|
result = create_node(rbt->mctx, &newname, &newnode);
|
|
|
|
|
|
|
|
else {
|
|
|
|
memcpy(NAME(down) + NAMELEN(down), NAME(node), NAMELEN(node));
|
|
|
|
NAMELEN(down) = newsize;
|
|
|
|
OFFSETLEN(down) = newlabels;
|
|
|
|
memcpy(OFFSETS(down), newname.offsets, newlabels);
|
|
|
|
|
|
|
|
newnode = down;
|
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
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));
|
1999-03-11 22:04:24 +00:00
|
|
|
|
|
|
|
if (newnode != down) {
|
|
|
|
isc_mem_put(rbt->mctx, down, NODE_SIZE(down));
|
|
|
|
isc_mem_put(rbt->mctx, r.base, r.length);
|
|
|
|
}
|
1999-01-31 00:52:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
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-03-03 20:01:49 +00:00
|
|
|
rotate_right(dns_rbtnode_t *node, dns_rbtnode_t *parent, dns_rbtnode_t **rootp)
|
|
|
|
{
|
1999-01-25 15:46:30 +00:00
|
|
|
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-03-12 05:00:32 +00:00
|
|
|
static void
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_rbt_addonlevel(dns_rbtnode_t *node,
|
|
|
|
dns_rbtnode_t *current, int order,
|
|
|
|
dns_rbtnode_t **rootp, dns_rbtnodechain_t *chain)
|
|
|
|
{
|
|
|
|
dns_rbtnode_t *child, *root, *tmp, *parent, *grandparent;
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_t add_name, current_name;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_offsets_t add_offsets, current_offsets;
|
1999-01-31 00:52:53 +00:00
|
|
|
unsigned int depth;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
REQUIRE(rootp != NULL);
|
1999-03-03 20:01:49 +00:00
|
|
|
REQUIRE(node != NULL && LEFT(node) == NULL && RIGHT(node) == NULL);
|
|
|
|
REQUIRE(current != NULL && LEFT(node) == NULL && RIGHT(node) == NULL);
|
1999-02-01 03:26:00 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
root = *rootp;
|
|
|
|
if (root == NULL) {
|
|
|
|
MAKE_BLACK(node);
|
|
|
|
*rootp = node;
|
1999-03-12 05:00:32 +00:00
|
|
|
return;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
child = root;
|
1999-01-31 19:55:52 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(&add_name, add_offsets);
|
1999-02-06 01:27:35 +00:00
|
|
|
NODENAME(node, &add_name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(¤t_name, current_offsets);
|
|
|
|
NODENAME(current, ¤t_name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
if (order < 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-03-03 20:01:49 +00:00
|
|
|
depth = chain->ancestor_count - 1;
|
|
|
|
|
1999-02-01 03:26:00 +00:00
|
|
|
while (node != root && IS_RED(chain->ancestors[depth])) {
|
1999-01-31 00:52:53 +00:00
|
|
|
INSIST(depth > 0);
|
1999-01-31 19:55:52 +00:00
|
|
|
|
1999-02-01 03:26:00 +00:00
|
|
|
parent = chain->ancestors[depth];
|
|
|
|
grandparent = chain->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;
|
1999-02-01 03:26:00 +00:00
|
|
|
chain->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-31 19:55:52 +00:00
|
|
|
rotate_right(grandparent,
|
1999-02-01 03:26:00 +00:00
|
|
|
chain->ancestors[depth - 2],
|
1999-01-25 15:46:30 +00:00
|
|
|
&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;
|
1999-02-01 03:26:00 +00:00
|
|
|
chain->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-31 19:55:52 +00:00
|
|
|
rotate_left(grandparent,
|
1999-02-01 03:26:00 +00:00
|
|
|
chain->ancestors[depth - 2],
|
1999-01-25 15:46:30 +00:00
|
|
|
&root);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MAKE_BLACK(root);
|
|
|
|
*rootp = root;
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
return;
|
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,
|
1999-02-06 01:27:35 +00:00
|
|
|
dns_rbtnodechain_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:
|
1999-03-30 01:56:01 +00:00
|
|
|
* + deleting everything DOWN from a node that is itself being deleted, and
|
1999-01-31 01:35:04 +00:00
|
|
|
* + 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,
|
1999-02-06 01:27:35 +00:00
|
|
|
* this function would need to adjusted accordingly.
|
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)
|
1999-02-06 01:27:35 +00:00
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
1999-01-31 00:52:53 +00:00
|
|
|
|
|
|
|
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_indent(int depth) {
|
1999-01-20 10:13:43 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < depth; i++)
|
|
|
|
putchar('\t');
|
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
1999-01-25 15:46:30 +00:00
|
|
|
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;
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_offsets_t offsets;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
r.length = NAMELEN(node);
|
|
|
|
r.base = NAME(node);
|
|
|
|
|
1999-03-03 20:01:49 +00:00
|
|
|
dns_name_init(&name, offsets);
|
1999-01-20 10:13:43 +00:00
|
|
|
dns_name_fromregion(&name, &r);
|
|
|
|
|
|
|
|
isc_buffer_init(&target, buffer, 255, ISC_BUFFERTYPE_TEXT);
|
1999-03-03 20:01:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ISC_FALSE means absolute names have the final dot added.
|
|
|
|
*/
|
|
|
|
dns_name_totext(&name, ISC_FALSE, &target);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
|
|
|
printf("%.*s", (int)target.used, (char *)target.base);
|
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
1999-01-25 15:46:30 +00:00
|
|
|
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) {
|
1999-03-12 05:00:32 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_printtree(rbt->root, NULL, 0);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Chain Functions
|
|
|
|
*/
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
static inline dns_result_t
|
|
|
|
chain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
|
|
|
|
isc_boolean_t include_chain_end)
|
|
|
|
{
|
|
|
|
dns_name_t nodename;
|
|
|
|
dns_result_t result;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
dns_name_init(&nodename, NULL);
|
|
|
|
|
|
|
|
result = DNS_R_SUCCESS;
|
|
|
|
|
|
|
|
for (i = 0; i < chain->level_count; i++) {
|
|
|
|
NODENAME(chain->levels[i], &nodename);
|
|
|
|
result = dns_name_concatenate(&nodename, name, name, NULL);
|
|
|
|
|
|
|
|
if (result != DNS_R_SUCCESS)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS && include_chain_end) {
|
|
|
|
NODENAME(chain->end, &nodename);
|
|
|
|
result = dns_name_concatenate(&nodename, name, name, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
void
|
|
|
|
dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
|
|
|
|
/*
|
|
|
|
* Initialize 'chain'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
REQUIRE(chain != NULL);
|
|
|
|
|
|
|
|
chain->mctx = mctx;
|
1999-04-09 15:21:15 +00:00
|
|
|
chain->end = NULL;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
chain->ancestors = chain->ancestor_block;
|
|
|
|
chain->ancestor_count = 0;
|
|
|
|
chain->ancestor_maxitems = DNS_RBT_ANCESTORBLOCK;
|
|
|
|
chain->level_count = 0;
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
chain->magic = CHAIN_MAGIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_rbtnode_t *
|
|
|
|
dns_rbtnodechain_current(dns_rbtnodechain_t *chain) {
|
|
|
|
return (chain->end);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_result_t
|
|
|
|
dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
|
|
|
|
dns_name_t *origin)
|
|
|
|
{
|
|
|
|
dns_rbtnode_t *current, *previous, *predecessor;
|
|
|
|
dns_result_t result = DNS_R_SUCCESS;
|
|
|
|
isc_boolean_t new_origin = ISC_FALSE;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
predecessor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
if (LEFT(current) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
while (RIGHT(current) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = RIGHT(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
predecessor = current;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
while (chain->ancestors[chain->ancestor_count - 1] != NULL) {
|
|
|
|
previous = current;
|
|
|
|
current = chain->ancestors[--chain->ancestor_count];
|
|
|
|
|
|
|
|
if (RIGHT(current) == previous) {
|
|
|
|
predecessor = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (predecessor != NULL) {
|
|
|
|
if (DOWN(predecessor) != NULL) {
|
|
|
|
/*
|
|
|
|
* The predecessor is really down at least one level.
|
|
|
|
* Go down and as far right as possible, and repeat
|
|
|
|
* as long as the rightmost node has a down pointer.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* XXX DCL Need to do something about origins
|
|
|
|
* here. See whether to go down, and if so
|
|
|
|
* whether it is truly what Bob calls a
|
|
|
|
* new origin.
|
|
|
|
*/
|
|
|
|
ADD_ANCESTOR(chain, NULL);
|
|
|
|
ADD_LEVEL(chain, predecessor);
|
|
|
|
predecessor = DOWN(predecessor);
|
|
|
|
|
|
|
|
/* XXX DCL duplicated from above; clever
|
|
|
|
* way to unduplicate? */
|
|
|
|
|
|
|
|
while (RIGHT(predecessor) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, predecessor);
|
|
|
|
predecessor = RIGHT(predecessor);
|
|
|
|
}
|
|
|
|
} while (DOWN(predecessor) != NULL);
|
|
|
|
|
|
|
|
/* XXX DCL probably needs work on the concept */
|
|
|
|
/* XXX origin needs to include itself */
|
|
|
|
if (origin != NULL) {
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (chain->level_count > 0) {
|
|
|
|
/*
|
|
|
|
* Got to the root of the tree without having traversed
|
|
|
|
* any right links. Ascend the tree one level.
|
|
|
|
*/
|
|
|
|
predecessor = chain->levels[--chain->level_count];
|
|
|
|
chain->ancestor_count--;
|
|
|
|
|
1999-04-09 22:49:46 +00:00
|
|
|
/* XXX DCL probably needs work on the concept */
|
|
|
|
if (origin && chain->level_count > 0) {
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
if (predecessor != NULL) {
|
|
|
|
chain->end = predecessor;
|
|
|
|
|
1999-04-09 22:55:20 +00:00
|
|
|
/* XXX DCL are full names desired, or just the
|
|
|
|
* name relative to the origin?
|
1999-04-09 15:21:15 +00:00
|
|
|
|
1999-04-09 22:55:20 +00:00
|
|
|
result = chain_name(chain, name, ISC_TRUE);
|
|
|
|
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
NODENAME(chain->end, name);
|
|
|
|
|
|
|
|
if (new_origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
result = DNS_R_NEWORIGIN;
|
|
|
|
} else
|
|
|
|
result = DNS_R_NOMORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_result_t
|
|
|
|
dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
|
|
|
|
dns_name_t *origin)
|
|
|
|
{
|
|
|
|
dns_rbtnode_t *current, *previous, *successor;
|
|
|
|
dns_result_t result = DNS_R_SUCCESS;
|
|
|
|
isc_boolean_t new_origin = ISC_FALSE;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
successor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX Need to do something about origins here -- See whether
|
|
|
|
* to go down, and if so whether it is a new origin.
|
|
|
|
*/
|
|
|
|
if (DOWN(current) != NULL) {
|
1999-04-09 22:49:46 +00:00
|
|
|
if (origin != NULL) {
|
|
|
|
result = chain_name(chain, origin, ISC_TRUE);
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
ADD_ANCESTOR(chain, NULL);
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
current = DOWN(current);
|
|
|
|
|
|
|
|
while (LEFT(current) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = LEFT(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
|
|
|
|
} else if (RIGHT(current) == NULL) {
|
|
|
|
while (chain->ancestors[chain->ancestor_count - 1] != NULL) {
|
|
|
|
previous = current;
|
|
|
|
current = chain->ancestors[--chain->ancestor_count];
|
|
|
|
|
|
|
|
if (LEFT(current) == previous) {
|
|
|
|
successor = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor == NULL) {
|
|
|
|
/*
|
|
|
|
* Reached the root without having traversed any
|
|
|
|
* left pointers, so this level is done.
|
|
|
|
* Ascend levels through the nodes' down pointers
|
|
|
|
* until one of those nodes has a right pointer.
|
|
|
|
* XXX more origin hand-waving here.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
while (chain->ancestors[--chain->ancestor_count]
|
|
|
|
!= NULL)
|
|
|
|
; /* Decrement to the level root. */
|
|
|
|
|
|
|
|
current = chain->levels[--chain->level_count];
|
|
|
|
} while (RIGHT(current) == NULL &&
|
|
|
|
chain->level_count > 0);
|
|
|
|
|
|
|
|
if (origin != NULL) {
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor == NULL && RIGHT(current) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
/* XXX duplicated from above; clever way to do it just once? */
|
|
|
|
while (LEFT(current) != NULL) {
|
|
|
|
ADD_ANCESTOR(chain, current);
|
|
|
|
current = LEFT(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == DNS_R_SUCCESS) {
|
|
|
|
if (successor != NULL) {
|
|
|
|
chain->end = successor;
|
|
|
|
|
1999-04-09 22:55:20 +00:00
|
|
|
/* XXX DCL are full names desired, or just the
|
|
|
|
* name relative to the origin?
|
|
|
|
|
|
|
|
result = chain_name(chain, name, ISC_TRUE);
|
|
|
|
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
NODENAME(chain->end, name);
|
1999-04-09 15:21:15 +00:00
|
|
|
|
1999-04-09 22:55:20 +00:00
|
|
|
if (new_origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
result = DNS_R_NEWORIGIN;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = DNS_R_NOMORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_result_t
|
|
|
|
dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
|
|
|
dns_name_t *name, dns_name_t *origin)
|
|
|
|
|
|
|
|
{
|
|
|
|
(void)chain;
|
|
|
|
(void)rbt;
|
|
|
|
(void)name;
|
|
|
|
(void)origin;
|
|
|
|
|
|
|
|
return (DNS_R_SUCCESS);
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
}
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_result_t
|
|
|
|
dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
|
|
|
dns_name_t *name, dns_name_t *origin)
|
|
|
|
|
|
|
|
{
|
|
|
|
(void)chain;
|
|
|
|
(void)rbt;
|
|
|
|
(void)name;
|
|
|
|
(void)origin;
|
|
|
|
|
|
|
|
return (DNS_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
void
|
|
|
|
dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
|
|
|
|
/*
|
|
|
|
* Free any dynamic storage associated with 'chain', and then
|
|
|
|
* reinitialize 'chain'.
|
|
|
|
*/
|
|
|
|
|
1999-04-09 15:21:15 +00:00
|
|
|
REQUIRE(VALID_CHAIN(chain));
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
|
|
|
put_ancestor_mem(chain);
|
1999-04-09 15:21:15 +00:00
|
|
|
chain->end = NULL;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
chain->ancestors = chain->ancestor_block;
|
|
|
|
chain->ancestor_count = 0;
|
|
|
|
chain->ancestor_maxitems = DNS_RBT_ANCESTORBLOCK;
|
|
|
|
chain->level_count = 0;
|
|
|
|
}
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
|
|
|
|
/*
|
|
|
|
* Free any dynamic storage associated with 'chain', and then
|
|
|
|
* invalidate 'chain'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
dns_rbtnodechain_reset(chain);
|
|
|
|
|
|
|
|
chain->magic = 0;
|
|
|
|
}
|