rbt.c revision c5944292e9ebee4a39fe939b9a16fe5596808556
1633838b8255282d10af15c5c84cee5a51466712Bob Halley/*
a7c412f37cc73d0332887a746e81220cbf09dd00Mark Andrews * Copyright (C) 1999, 2000 Internet Software Consortium.
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews *
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * Permission to use, copy, modify, and distribute this software for any
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * purpose with or without fee is hereby granted, provided that the above
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * copyright notice and this permission notice appear in all copies.
1633838b8255282d10af15c5c84cee5a51466712Bob Halley *
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1633838b8255282d10af15c5c84cee5a51466712Bob Halley */
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
28a8f5b0de57d269cf2845c69cb6abe18cbd3b3aMark Andrews/* $Id: rbt.c,v 1.89 2000/07/31 23:27:24 tale Exp $ */
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein/* Principal Authors: DCL */
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence
d25afd60ee2286cb171c4960a790f3d7041b6f85Bob Halley#include <config.h>
d25afd60ee2286cb171c4960a790f3d7041b6f85Bob Halley
94e25967cda41b886e33ec254b917d21df21a187Bob Halley#include <isc/mem.h>
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence#include <isc/string.h>
903247531a10d699ef239a7351554ba0a1e3cd22Evan Hunt#include <isc/util.h>
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence/*
94e25967cda41b886e33ec254b917d21df21a187Bob Halley * This define is so dns/name.h (included by dns/fixedname.h) uses more
6e3a8256eed85f6704861d269ccfb35bdaeed5ffDavid Lawrence * efficient macro calls instead of functions for a few operations.
6e3a8256eed85f6704861d269ccfb35bdaeed5ffDavid Lawrence */
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews#define DNS_NAME_USEINLINE 1
33f87146a856eb6c3dfd55a8ee9c173c94f82150Andreas Gustafsson
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews#include <dns/fixedname.h>
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence#include <dns/rbt.h>
94e25967cda41b886e33ec254b917d21df21a187Bob Halley#include <dns/result.h>
a147de10fe5e19e593d42152ffd6879eca69860dEvan Hunt
364a82f7c25b62967678027043425201a5e5171aBob Halley#define RBT_MAGIC 0x5242542BU /* RBT+. */
94e25967cda41b886e33ec254b917d21df21a187Bob Halley#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC)
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein/*
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein * XXXDCL Since parent pointers were added in again, I could remove all of the
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode,
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * _lastnode. This would involve pretty major change to the API.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence#define CHAIN_MAGIC 0x302d302dU /* 0-0-. */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC)
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestruct dns_rbt {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence unsigned int magic;
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews isc_mem_t * mctx;
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews dns_rbtnode_t * root;
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews void (*data_deleter)(void *, void *);
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews void * deleter_arg;
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein};
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define RED 0
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define BLACK 1
27809a2ee5db141b684e53bf1d94da26e9f92d3aMark Andrews
27809a2ee5db141b684e53bf1d94da26e9f92d3aMark Andrews/*
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley * Elements of the rbtnode structure.
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews */
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define IS_ROOT(node) ((node)->is_root)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define PARENT(node) ((node)->parent)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define LEFT(node) ((node)->left)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define RIGHT(node) ((node)->right)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define DOWN(node) ((node)->down)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define DATA(node) ((node)->data)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define FINDCALLBACK(node) ((node)->find_callback)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define COLOR(node) ((node)->color)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define NAMELEN(node) ((node)->namelen)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define OFFSETLEN(node) ((node)->offsetlen)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define ATTRS(node) ((node)->attributes)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define PADBYTES(node) ((node)->padbytes)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews/*
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews * Structure elements from the rbtdb.c, not
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews * used as part of the rbt.c algorithms.
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews */
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define DIRTY(node) ((node)->dirty)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define WILD(node) ((node)->wild)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define LOCKNUM(node) ((node)->locknum)
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews#define REFS(node) ((node)->references)
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews/*
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews * The variable length stuff stored after the node.
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews */
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define NAME(node) ((unsigned char *)((node) + 1))
3740b569ae76295b941d57a724a43beb75b533baBob Halley#define OFFSETS(node) (NAME(node) + NAMELEN(node))
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence#define NODE_SIZE(node) (sizeof(*node) + \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley NAMELEN(node) + OFFSETLEN(node) + PADBYTES(node))
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley/*
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley * Color management.
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley */
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define IS_RED(node) ((node) != NULL && (node)->color == RED)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define MAKE_RED(node) ((node)->color = RED)
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson#define MAKE_BLACK(node) ((node)->color = BLACK)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence/*
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley * Chain management.
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley */
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define ADD_ANCESTOR(chain, node) \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halleydo { \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley if ((chain)->ancestor_count == (chain)->ancestor_maxitems && \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley get_ancestor_mem(chain) != ISC_R_SUCCESS) { \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley dns_rbtnodechain_invalidate(chain); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley return (ISC_R_NOMEMORY); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley } \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley (chain)->ancestors[(chain)->ancestor_count++] = (node); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley} while (0)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
27809a2ee5db141b684e53bf1d94da26e9f92d3aMark Andrews#define ADD_LEVEL(chain, node) \
27809a2ee5db141b684e53bf1d94da26e9f92d3aMark Andrews (chain)->levels[(chain)->level_count++] = (node)
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence/*
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence * The following macros directly access normally private name variables.
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence * These macros are used to avoid a lot of function calls in the critical
232fd751edcb5dd2b1fd2666e039efe83d2e2b55Michael Sawyer * path of the tree traversal code.
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence */
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence#define NODENAME(node, name) \
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrencedo { \
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence (name)->length = NAMELEN(node); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley (name)->labels = OFFSETLEN(node); \
3740b569ae76295b941d57a724a43beb75b533baBob Halley (name)->ndata = NAME(node); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley (name)->offsets = OFFSETS(node); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley (name)->attributes = ATTRS(node); \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley (name)->attributes |= DNS_NAMEATTR_READONLY; \
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley} while (0)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#ifdef DEBUG
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#define inline
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson/*
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley * A little something to help out in GDB.
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence */
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halleydns_name_t Name(dns_rbtnode_t *node);
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halleydns_name_t
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob HalleyName(dns_rbtnode_t *node) {
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley dns_name_t name;
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley dns_name_init(&name, NULL);
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley if (node != NULL)
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews NODENAME(node, &name);
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley return (name);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley}
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews
94e25967cda41b886e33ec254b917d21df21a187Bob Halleystatic void dns_rbt_printnodename(dns_rbtnode_t *node);
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley#endif
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence
94e25967cda41b886e33ec254b917d21df21a187Bob Halley#ifdef RBT_MEM_TEST
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews#undef DNS_RBT_ANCESTORBLOCK
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews#define DNS_RBT_ANCESTORBLOCK 1 /* To give the reallocation code a workout. */
94e25967cda41b886e33ec254b917d21df21a187Bob Halley#endif
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
94e25967cda41b886e33ec254b917d21df21a187Bob Halley/*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * Forward declarations.
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic isc_result_t
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencecreate_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic isc_result_t
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencejoin_nodes(dns_rbt_t *rbt, dns_rbtnode_t *node);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrewsstatic inline isc_result_t
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrewsget_ancestor_mem(dns_rbtnodechain_t *chain);
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrewsstatic inline void
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrewsput_ancestor_mem(dns_rbtnodechain_t *chain);
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrews
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic inline void
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencerotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
df0f58959ed82a2a43ca8d816ce9592541df9f2fMark Andrewsstatic inline void
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencerotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrencestatic void
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencedns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbtnode_t **rootp, dns_rbtnodechain_t *chain);
5eb91bd90e3ad3426e5e3213031556a737cf3809Mark Andrews
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic void
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencedns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp);
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic void
94e25967cda41b886e33ec254b917d21df21a187Bob Halleydns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
94e25967cda41b886e33ec254b917d21df21a187Bob Halley/*
94e25967cda41b886e33ec254b917d21df21a187Bob Halley * Initialize a red/black tree of trees.
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley */
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafssonisc_result_t
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halleydns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews void *deleter_arg, dns_rbt_t **rbtp)
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley{
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley dns_rbt_t *rbt;
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence REQUIRE(mctx != NULL);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence REQUIRE(rbtp != NULL && *rbtp == NULL);
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews
4b87939256ede703385e9cab92d3c58d03c31098Mark Andrews rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley if (rbt == NULL)
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley return (ISC_R_NOMEMORY);
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->mctx = mctx;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->data_deleter = deleter;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->deleter_arg = deleter_arg;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->root = NULL;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->magic = RBT_MAGIC;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence *rbtp = rbt;
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews return (ISC_R_SUCCESS);
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews}
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrews/*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * Deallocate a red/black tree of trees.
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence */
70ec7dd74103fa9e92a6d56a0e3b0fc30e17af0dMark Andrewsvoid
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencedns_rbt_destroy(dns_rbt_t **rbtp) {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbt_t *rbt;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence REQUIRE(rbtp != NULL && VALID_RBT(*rbtp));
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt = *rbtp;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbt_deletetree(rbt, rbt->root);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->magic = 0;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley isc_mem_put(rbt->mctx, rbt, sizeof(*rbt));
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
9e8947d9e606b967d0792d0ab1ee7afac5e5f39dMark Andrews *rbtp = NULL;
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley}
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley/*
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley * The next three functions for chains, get_ancestor_mem, put_ancestor_mem
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley * and chain_name, appear early in this file so they can be effectively
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley * inlined by the other rbt functions that use them.
afdb3abb9b06ed4070ac9021f1f4427b4cb3a286Bob Halley */
94e25967cda41b886e33ec254b917d21df21a187Bob Halleystatic inline isc_result_t
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafssonget_ancestor_mem(dns_rbtnodechain_t *chain) {
bf6d2e39124ab3d51c253f7acad9a4abef059be6Bob Halley dns_rbtnode_t **ancestor_mem;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence int oldsize, newsize;
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
bf6d2e39124ab3d51c253f7acad9a4abef059be6Bob Halley oldsize = chain->ancestor_maxitems * sizeof(dns_rbtnode_t *);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley newsize = oldsize + DNS_RBT_ANCESTORBLOCK * sizeof(dns_rbtnode_t *);
bf6d2e39124ab3d51c253f7acad9a4abef059be6Bob Halley
94e25967cda41b886e33ec254b917d21df21a187Bob Halley if (oldsize == 0) {
bf6d2e39124ab3d51c253f7acad9a4abef059be6Bob Halley chain->ancestors = chain->ancestor_block;
94e25967cda41b886e33ec254b917d21df21a187Bob Halley } else {
bf6d2e39124ab3d51c253f7acad9a4abef059be6Bob Halley ancestor_mem = isc_mem_get(chain->mctx, newsize);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
94e25967cda41b886e33ec254b917d21df21a187Bob Halley if (ancestor_mem == NULL)
94e25967cda41b886e33ec254b917d21df21a187Bob Halley return (ISC_R_NOMEMORY);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence memcpy(ancestor_mem, chain->ancestors, oldsize);
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson if (chain->ancestor_maxitems > DNS_RBT_ANCESTORBLOCK)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley isc_mem_put(chain->mctx, chain->ancestors, oldsize);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence chain->ancestors = ancestor_mem;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence }
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence chain->ancestor_maxitems += DNS_RBT_ANCESTORBLOCK;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence return (ISC_R_SUCCESS);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence}
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence/*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * This is used by functions that are popping the chain off their
94e25967cda41b886e33ec254b917d21df21a187Bob Halley * own stack, and so do not need to have ancestor_maxitems or the
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley * ancestors pointer reset. Functions that will be reusing a chain
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley * structure need to call dns_rbtnodechain_reset() instead.
c89ac488df58cf6a37918cd00236eedf015830f8Andreas Gustafsson */
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halleystatic inline void
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrenceput_ancestor_mem(dns_rbtnodechain_t *chain) {
94e25967cda41b886e33ec254b917d21df21a187Bob Halley if (chain->ancestor_maxitems > DNS_RBT_ANCESTORBLOCK)
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence isc_mem_put(chain->mctx, chain->ancestors,
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence chain->ancestor_maxitems
94e25967cda41b886e33ec254b917d21df21a187Bob Halley * sizeof(dns_rbtnode_t *));
94e25967cda41b886e33ec254b917d21df21a187Bob Halley}
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafssonstatic inline isc_result_t
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafssonchain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson isc_boolean_t include_chain_end)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley{
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_name_t nodename;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence isc_result_t result = ISC_R_SUCCESS;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence unsigned int i;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_name_init(&nodename, NULL);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_name_reset(name);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley for (i = 0; i < chain->level_count; i++) {
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley NODENAME(chain->levels[i], &nodename);
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley result = dns_name_concatenate(&nodename, name, name, NULL);
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence if (result != ISC_R_SUCCESS)
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley break;
4bed2e84a34b37259b85e5c092d51c122ef58c3cBob Halley }
94e25967cda41b886e33ec254b917d21df21a187Bob Halley
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence if (result == ISC_R_SUCCESS &&
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence include_chain_end && chain->end != NULL) {
94e25967cda41b886e33ec254b917d21df21a187Bob Halley NODENAME(chain->end, &nodename);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence result = dns_name_concatenate(&nodename, name, name, NULL);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence }
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence return (result);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence}
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencestatic inline isc_result_t
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrencemove_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence do {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence /*
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence * Go as far right and then down as much as possible,
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence * as long as the rightmost node has a down pointer.
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence */
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence while (RIGHT(node) != NULL) {
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence ADD_ANCESTOR(chain, node);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence node = RIGHT(node);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence }
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence if (DOWN(node) == NULL)
0874abad14e3e9ecfc3dc1a1a2b9969f2f027724Mark Andrews break;
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence ADD_ANCESTOR(chain, NULL);
4ad9b25e6ddf948ffb3b8198c5540d251f26c52eDavid Lawrence ADD_LEVEL(chain, node);
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence node = DOWN(node);
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence } while (1);
ae5df22719a9e2c252ea1fcccd2cadb44c8bd8d4Mark Andrews
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence chain->end = node;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence return (ISC_R_SUCCESS);
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence}
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence/*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * Add 'name' to tree, initializing its data pointer with 'data'.
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrenceisc_result_t
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrencedns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence /*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * Does this thing have too many variables or what?
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbtnode_t **root, *parent, *child, *current, *new_current;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_name_t *add_name, current_name, *prefix, *suffix;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_offsets_t current_offsets;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_namereln_t compared;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence isc_result_t result = ISC_R_SUCCESS;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbtnodechain_t chain;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence unsigned int common_labels, common_bits, add_bits;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence int order;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence REQUIRE(VALID_RBT(rbt));
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence REQUIRE(dns_name_isabsolute(name));
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence REQUIRE(nodep != NULL && *nodep == NULL);
269c07173e24d7811e2fd09304023e3104fcbe0bMark Andrews
269c07173e24d7811e2fd09304023e3104fcbe0bMark Andrews /*
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * Create a copy of the name so the original name structure is
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * not modified. The name data needs to be modifiable when
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence * a node is split on a bitstring label.
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence */
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_fixedname_init(&fixedcopy);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence add_name = dns_fixedname_name(&fixedcopy);
5fa46bc91672ef5737aee6f99763161511566c24Tinderbox User dns_name_clone(name, add_name);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence if (rbt->root == NULL) {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence result = create_node(rbt->mctx, add_name, &new_current);
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence if (result == ISC_R_SUCCESS) {
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence IS_ROOT(new_current) = ISC_TRUE;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence rbt->root = new_current;
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence *nodep = new_current;
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence }
26f327f1f53afdb8256affa1c197ed138bf3cb2fAndreas Gustafsson return (result);
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence }
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
6fa1cb5754695d550a58c6e8978fda65f5146af7David Lawrence dns_rbtnodechain_init(&chain, rbt->mctx);
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence ADD_ANCESTOR(&chain, NULL);
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence
114d0d1642b5ede0ab154532159fe38c30762d82David Lawrence dns_fixedname_init(&fixedprefix);
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson dns_fixedname_init(&fixedsuffix);
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson prefix = dns_fixedname_name(&fixedprefix);
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson suffix = dns_fixedname_name(&fixedsuffix);
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson root = &rbt->root;
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson INSIST(IS_ROOT(*root));
6184f9fc1e65ef131ea308a1a92882595bb1aeeaAndreas Gustafsson parent = NULL;
6184f9fc1e65ef131ea308a1a92882595bb1aeeaAndreas Gustafsson current = NULL;
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson child = *root;
c36f45e354c0d5b6ab9f821bfe315d0ce9d95a29Mark Andrews dns_name_init(&current_name, current_offsets);
ff6e834f7d48d4b116627ecf8cafa0fbacc25bd4Andreas Gustafsson do {
ff6e834f7d48d4b116627ecf8cafa0fbacc25bd4Andreas Gustafsson current = child;
ff6e834f7d48d4b116627ecf8cafa0fbacc25bd4Andreas Gustafsson
ff6e834f7d48d4b116627ecf8cafa0fbacc25bd4Andreas Gustafsson INSIST((IS_ROOT(current) &&
a6f0e9c985220f0e4509777e6528afb64e0ad576Mukund Sivaraman chain.ancestors[chain.ancestor_count - 1] == NULL) ||
a6f0e9c985220f0e4509777e6528afb64e0ad576Mukund Sivaraman (! IS_ROOT(current) &&
a6f0e9c985220f0e4509777e6528afb64e0ad576Mukund Sivaraman chain.ancestors[chain.ancestor_count - 1] ==
a6f0e9c985220f0e4509777e6528afb64e0ad576Mukund Sivaraman PARENT(current)));
c8563aaf86c04f0e2284bcc8e444a0651c157ea0Andreas Gustafsson
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews NODENAME(current, &current_name);
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews compared = dns_name_fullcompare(add_name, &current_name,
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews &order,
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews &common_labels, &common_bits);
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews if (compared == dns_namereln_equal) {
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews *nodep = current;
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews result = ISC_R_EXISTS;
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews break;
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews }
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews if (compared == dns_namereln_none) {
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt if (order < 0) {
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt parent = current;
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt child = LEFT(current);
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt ADD_ANCESTOR(&chain, current);
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt } else if (order > 0) {
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt parent = current;
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt child = RIGHT(current);
a147de10fe5e19e593d42152ffd6879eca69860dEvan Hunt ADD_ANCESTOR(&chain, current);
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt }
a147de10fe5e19e593d42152ffd6879eca69860dEvan Hunt
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt } else {
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt /*
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt * This name has some suffix in common with the
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt * name at the current node. If the name at
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt * the current node is shorter, that means the
789252d55f025db52ee02aa933c9f09a4aadfa97Evan Hunt * new name should be in a subtree. If the
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * name at the current node is longer, that means
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * the down pointer to this tree should point
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * to a new tree that has the common suffix, and
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * the non-common parts of these two names should
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * start a new tree.
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews */
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews if (compared == dns_namereln_subdomain) {
96ea71632887c58a9d00f47eb318bf76b35903c3Mark Andrews /*
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * All of the existing labels are in common,
186e7f37c9fc985a7a7264cc8170e48a25bed434Mark Andrews * so the new name is in a subtree.
* Whack off the common labels for the
* not-in-common part to be searched for
* in the next level.
*/
result = dns_name_split(add_name,
common_labels,
common_bits,
add_name, NULL);
if (result != ISC_R_SUCCESS)
break;
/*
* Follow the down pointer (possibly NULL).
*/
root = &DOWN(current);
INSIST(*root == NULL ||
(IS_ROOT(*root) &&
PARENT(*root) == current));
parent = NULL;
child = DOWN(current);
ADD_ANCESTOR(&chain, NULL);
ADD_LEVEL(&chain, current);
} 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.
*/
INSIST(compared == dns_namereln_commonancestor
|| compared == dns_namereln_contains);
/*
* 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))) {
result = ISC_R_NOSPACE;
break;
}
/*
* Split the name into two parts, a prefix
* which is the not-in-common parts of the
* two names and a suffix that is the common
* parts of them.
*/
result = dns_name_split(&current_name,
common_labels,
common_bits,
prefix, suffix);
if (result == ISC_R_SUCCESS)
result = create_node(rbt->mctx, suffix,
&new_current);
if (result != ISC_R_SUCCESS)
break;
/*
* Reproduce the tree attributes of the
* current node.
*/
IS_ROOT(new_current) = IS_ROOT(current);
PARENT(new_current) = PARENT(current);
LEFT(new_current) = LEFT(current);
RIGHT(new_current) = RIGHT(current);
COLOR(new_current) = COLOR(current);
/*
* Fix pointers that were to the current node.
*/
if (parent != NULL) {
if (LEFT(parent) == current)
LEFT(parent) = new_current;
else
RIGHT(parent) = new_current;
}
if (LEFT(new_current) != NULL)
PARENT(LEFT(new_current)) =
new_current;
if (RIGHT(new_current) != NULL)
PARENT(RIGHT(new_current)) =
new_current;
if (*root == current)
*root = new_current;
/*
* Now make the new root of the subtree
* as the not-in-common labels of the current
* 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.
*
* 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.
*
* When bitstring labels are involved, things
* are just a tad more complicated (aren't
* they always?) because the splitting
* has shifted the bits that this name needs,
* as well as adjusted the bit count.
* So there are convolutions to deal with it.
* There are compromises here between
* abstraction and efficiency.
*/
if (common_bits > 0) {
dns_label_t label;
dns_name_getlabel(prefix,
dns_name_countlabels(&current_name)
- common_labels,
&label);
INSIST(dns_label_type(&label) ==
dns_labeltype_bitstring);
memcpy(NAME(current) +
(label.base - prefix->ndata),
label.base,
label.length);
dns_name_getlabel(add_name,
dns_name_countlabels(add_name)
- common_labels,
&label);
INSIST(dns_label_type(&label) ==
dns_labeltype_bitstring);
add_bits = dns_label_countbits(&label);
} else
add_bits = 0;
NAMELEN(current) = prefix->length;
OFFSETLEN(current) = prefix->labels;
memcpy(OFFSETS(current), prefix->offsets,
prefix->labels);
PADBYTES(current) +=
(current_name.length - prefix->length) +
(current_name.labels - prefix->labels);
/*
* Set up the new root of the next level.
* By definition it will not be the top
* level tree, so clear DNS_NAMEATTR_ABSOLUTE.
*/
IS_ROOT(current) = ISC_TRUE;
PARENT(current) = new_current;
DOWN(new_current) = current;
root = &DOWN(new_current);
ADD_ANCESTOR(&chain, NULL);
ADD_LEVEL(&chain, new_current);
LEFT(current) = NULL;
RIGHT(current) = NULL;
MAKE_BLACK(current);
ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
if (common_labels ==
dns_name_countlabels(add_name) &&
common_bits == add_bits) {
/*
* The name has been added by pushing
* the not-in-common parts down to
* a new level.
*/
*nodep = new_current;
put_ancestor_mem(&chain);
return (ISC_R_SUCCESS);
} else {
/*
* The current node has no data,
* because it is just a placeholder.
* Its data pointer is already NULL
* from create_node()), so there's
* nothing more to do to but add it to
* the ancestor chain, because it will
* be the parent node of the new node.
*/
ADD_ANCESTOR(&chain, current);
/* The not-in-common parts of the new
* name will be inserted into the new
* level following this loop (unless
* result != ISC_R_SUCCESS, which
* is tested after the loop ends).
*/
result = dns_name_split(add_name,
common_labels,
common_bits,
add_name,
NULL);
break;
}
}
}
} while (child != NULL);
if (result == ISC_R_SUCCESS)
result = create_node(rbt->mctx, add_name, &new_current);
if (result == ISC_R_SUCCESS) {
dns_rbt_addonlevel(new_current, current, order, root, &chain);
*nodep = new_current;
}
put_ancestor_mem(&chain);
return (result);
}
/*
* Add a name to the tree of trees, associating it with some data.
*/
isc_result_t
dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
isc_result_t result;
dns_rbtnode_t *node;
REQUIRE(VALID_RBT(rbt));
REQUIRE(dns_name_isabsolute(name));
node = NULL;
result = dns_rbt_addnode(rbt, name, &node);
/*
* 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 == ISC_R_SUCCESS ||
(result == ISC_R_EXISTS && DATA(node) == NULL)) {
DATA(node) = data;
result = ISC_R_SUCCESS;
}
return (result);
}
/*
* Find the node for "name" in the tree of trees.
*/
isc_result_t
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
unsigned int options, dns_rbtfindcallback_t callback,
void *callback_arg)
{
dns_rbtnode_t *current;
dns_rbtnodechain_t localchain;
dns_name_t *search_name, current_name, *callback_name;
dns_fixedname_t fixedcallbackname, fixedsearchname;
dns_namereln_t compared;
isc_result_t result, saved_result;
unsigned int common_labels, common_bits;
int order;
REQUIRE(VALID_RBT(rbt));
REQUIRE(dns_name_isabsolute(name));
REQUIRE(node != NULL && *node == NULL);
REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR))
!= (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR));
/*
* If there is a chain it needs to appear to be in a sane state,
* otherwise a chain is still needed to generate foundname and
* callback_name.
*/
if (chain == NULL) {
options |= DNS_RBTFIND_NOPREDECESSOR;
chain = &localchain;
dns_rbtnodechain_init(chain, rbt->mctx);
} else
dns_rbtnodechain_reset(chain);
if (rbt->root == NULL)
return (ISC_R_NOTFOUND);
dns_fixedname_init(&fixedcallbackname);
callback_name = dns_fixedname_name(&fixedcallbackname);
/*
* search_name is the name segment being sought in each tree level.
* By using a fixedname, the search_name will definitely have offsets
* and a buffer for use by any splitting that happens in the middle
* of a bitstring label. By using dns_name_clone, no name data is
* copied unless a bitstring split occurs.
*/
dns_fixedname_init(&fixedsearchname);
search_name = dns_fixedname_name(&fixedsearchname);
dns_name_clone(name, search_name);
dns_name_init(&current_name, NULL);
ADD_ANCESTOR(chain, NULL);
saved_result = ISC_R_SUCCESS;
current = rbt->root;
while (current != NULL) {
NODENAME(current, &current_name);
compared = dns_name_fullcompare(search_name, &current_name,
&order,
&common_labels, &common_bits);
if (compared == dns_namereln_equal)
break;
if (compared == dns_namereln_none) {
ADD_ANCESTOR(chain, current);
/*
* Standard binary search tree movement.
*/
if (order < 0)
current = LEFT(current);
else
current = RIGHT(current);
} else {
/*
* The names have some common suffix labels.
*
* 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.
*/
if (compared == dns_namereln_subdomain) {
/*
* Whack off the current node's common parts
* for the name to search in the next level.
*/
result = dns_name_split(search_name,
common_labels,
common_bits,
search_name, NULL);
if (result != ISC_R_SUCCESS) {
dns_rbtnodechain_reset(chain);
return (result);
}
/*
* This might be the closest enclosing name.
*/
if (DATA(current) != NULL ||
(options & DNS_RBTFIND_EMPTYDATA) != 0)
*node = current;
/*
* Point the chain to the next level. This
* needs to be done before 'current' is pointed
* there because the callback in the next
* block of code needs the current 'current',
* but in the event the callback requests that
* the search be stopped then the
* DNS_R_PARTIALMATCH code at the end of this
* function needs the chain pointed to the
* next level.
*/
ADD_ANCESTOR(chain, NULL);
ADD_LEVEL(chain, current);
/*
* 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 &&
FINDCALLBACK(current)) {
result = chain_name(chain,
callback_name,
ISC_FALSE);
if (result != ISC_R_SUCCESS) {
dns_rbtnodechain_reset(chain);
return (result);
}
result = (callback)(current,
callback_name,
callback_arg);
if (result != DNS_R_CONTINUE) {
saved_result = result;
/*
* Treat this node as if it
* had no down pointer.
*/
current = NULL;
break;
}
}
/*
* Finally, head to the next tree level.
*/
current = DOWN(current);
} else {
/*
* Though there are labels in common, the
* entire name at this node is not common
* with the search name so the search
* name does not exist in the tree.
* Add this node to the ancestor chain
* to simplify things for the chain fixing
* logic below then end the loop.
*/
INSIST(compared == dns_namereln_commonancestor
|| compared == dns_namereln_contains);
ADD_ANCESTOR(chain, current);
current = NULL;
}
}
}
/*
* If current is not NULL, NOEXACT is not disallowing exact matches,
* and either the node has data or an empty node is ok, return
* ISC_R_SUCCESS to indicate an exact match.
*/
if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 &&
(DATA(current) != NULL ||
(options & DNS_RBTFIND_EMPTYDATA) != 0)) {
/*
* Found an exact match.
*/
chain->end = current;
chain->level_matches = chain->level_count;
if (foundname != NULL)
result = chain_name(chain, foundname, ISC_TRUE);
else
result = ISC_R_SUCCESS;
if (result == ISC_R_SUCCESS) {
*node = current;
result = saved_result;
} else
*node = NULL;
} else {
/*
* Did not find an exact match (or did not want one).
*/
if (*node != NULL) {
/*
* ... but found a partially matching superdomain.
* Unwind the chain to the partial match node
* to set level_matches to the level above the node,
* and then to derive the name.
*
* chain->level_count is guaranteed to be at least 1
* here because by definition of finding a superdomain,
* the chain is pointed to at least the first subtree.
*/
chain->level_matches = chain->level_count - 1;
while (chain->levels[chain->level_matches] != *node) {
INSIST(chain->level_matches > 0);
chain->level_matches--;
}
if (foundname != NULL) {
unsigned int saved_count = chain->level_count;
chain->level_count = chain->level_matches + 1;
result = chain_name(chain, foundname,
ISC_FALSE);
chain->level_count = saved_count;
} else
result = ISC_R_SUCCESS;
if (result == ISC_R_SUCCESS)
result = DNS_R_PARTIALMATCH;
} else
result = ISC_R_NOTFOUND;
if (current != NULL) {
/*
* There was an exact match but either
* DNS_RBTFIND_NOEXACT was set, or
* DNS_RBTFIND_EMPTYDATA was set and the node had no
* data. A policy decision was made to set the
* chain to the exact match, but this is subject
* to change if it becomes apparent that something
* else would be more useful. It is important that
* this case is handled here, because the predecessor
* setting code below assumes the match was not exact.
*/
INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) ||
((options & DNS_RBTFIND_EMPTYDATA) == 0 &&
DATA(current) == NULL));
chain->end = current;
} else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) {
/*
* Ensure the chain points nowhere.
*/
chain->end = NULL;
} else {
/*
* Since there was no exact match, the chain argument
* needs to be pointed at the DNSSEC predecessor of
* the search name.
*
* First, point current to the node that stopped the
* search, and remove that node from the ancestor
* history.
*/
INSIST(chain->ancestor_count > 0);
current = chain->ancestors[--chain->ancestor_count];
if (current == NULL) {
/*
* Attempted to follow a down pointer that was
* NULL, which means the searched for name was
* a subdomain of a terminal name in the tree.
* Since there are no existing subdomains to
* order against, the terminal name is the
* predecessor.
*/
INSIST(chain->level_count > 0);
INSIST(chain->level_matches <
chain->level_count);
chain->end =
chain->levels[--chain->level_count];
} else {
isc_result_t result2;
/*
* Reached a point within a level tree that
* positively indicates the name is not
* present, but the stop node could be either
* less than the desired name (order > 0) or
* greater than the desired name (order < 0).
*
* If the stop node is less, it is not
* necessarily the predecessor. If the stop
* node has a down pointer, then the real
* predecessor is at the end of a level below
* (not necessarily the next level).
* Move down levels until the rightmost node
* does not have a down pointer.
*
* When the stop node is greater, it is
* the successor. All the logic for finding
* the predecessor is handily encapsulated
* in dns_rbtnodechain_prev. In the event
* that the search name is less than anything
* else in the tree, the chain is reset.
* XXX DCL What is the best way for the caller
* to know that the search name has
* no predecessor?
*/
if (order > 0) {
if (DOWN(current) != NULL) {
ADD_ANCESTOR(chain, NULL);
ADD_LEVEL(chain, current);
result2 =
move_chain_to_last(chain,
DOWN(current));
if (result2 != ISC_R_SUCCESS)
result = result2;
} else
/*
* Ah, the pure and simple
* case. The stop node is the
* predecessor.
*/
chain->end = current;
} else {
INSIST(order < 0);
chain->end = current;
result2 = dns_rbtnodechain_prev(chain,
NULL,
NULL);
if (result2 == ISC_R_SUCCESS ||
result2 == DNS_R_NEWORIGIN)
; /* Nothing. */
else if (result2 == ISC_R_NOMORE)
/*
* There is no predecessor.
*/
dns_rbtnodechain_reset(chain);
else
result = result2;
}
}
}
}
if (chain == &localchain)
put_ancestor_mem(chain);
return (result);
}
/*
* Get the data pointer associated with 'name'.
*/
isc_result_t
dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
dns_name_t *foundname, void **data) {
dns_rbtnode_t *node = NULL;
isc_result_t result;
REQUIRE(data != NULL && *data == NULL);
result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,
options, NULL, NULL);
if (node != NULL &&
(DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0))
*data = DATA(node);
else
result = ISC_R_NOTFOUND;
return (result);
}
/*
* Delete a name from the tree of trees.
*/
isc_result_t
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
dns_rbtnode_t *node = NULL;
isc_result_t result;
REQUIRE(VALID_RBT(rbt));
REQUIRE(dns_name_isabsolute(name));
/*
* First, find the node.
*
* When searching, the name might not have an exact match:
* consider a.b.a.com, b.b.a.com and c.b.a.com as the only
* elements of a tree, which would make layer 1 a single
* node tree of "b.a.com" and layer 2 a three node tree of
* a, b, and c. Deleting a.com would find only a partial depth
* match in the first layer. Should it be a requirement that
* that the name to be deleted have data? For now, it is.
*
* ->dirty, ->locknum and ->references are ignored; they are
* solely the province of rbtdb.c.
*/
result = dns_rbt_findnode(rbt, name, NULL, &node, NULL,
DNS_RBTFIND_NOOPTIONS, NULL, NULL);
if (result == ISC_R_SUCCESS)
if (DATA(node) != NULL)
result = dns_rbt_deletenode(rbt, node, recurse);
else
result = ISC_R_NOTFOUND;
else if (result == DNS_R_PARTIALMATCH)
result = ISC_R_NOTFOUND;
return (result);
}
/*
* Remove a node from the tree of trees and rejoin any levels, if possible.
*/
isc_result_t
dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
{
dns_rbtnode_t *parent, *root;
isc_result_t result;
REQUIRE(VALID_RBT(rbt));
REQUIRE(node != NULL);
if (DOWN(node) != NULL) {
if (recurse)
dns_rbt_deletetree(rbt, DOWN(node));
else {
if (rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node),
rbt->deleter_arg);
DATA(node) = NULL;
if (LEFT(DOWN(node)) != NULL ||
RIGHT(DOWN(node)) != NULL)
/*
* 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 (ISC_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.
*/
result = join_nodes(rbt, node);
return (result);
}
}
/*
* Note the node that points to the level of the node that is being
* deleted. If the deleted node is the top level, parent will be set
* to NULL.
*/
for (root = node; ! IS_ROOT(root); root = PARENT(root))
; /* Nothing. */
parent = PARENT(root);
/*
* 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.
*/
dns_rbt_deletefromlevel(node, parent == NULL ? &rbt->root :
&DOWN(parent));
if (rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node), rbt->deleter_arg);
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
/*
* 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, to the node that points to the level of the deleted node.
*/
node = parent;
if (node != NULL && DATA(node) == NULL &&
LEFT(DOWN(node)) == NULL && RIGHT(DOWN(node)) == NULL)
result = join_nodes(rbt, node);
else
result = ISC_R_SUCCESS;
return (result);
}
void
dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) {
REQUIRE(name->offsets == NULL);
NODENAME(node, name);
}
static isc_result_t
create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
dns_rbtnode_t *node;
isc_region_t region;
unsigned int labels;
REQUIRE(name->offsets != NULL);
dns_name_toregion(name, &region);
labels = dns_name_countlabels(name);
ENSURE(labels > 0);
/*
* Allocate space for the node structure, the name, and the offsets.
*/
node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) +
region.length + labels);
if (node == NULL)
return (ISC_R_NOMEMORY);
IS_ROOT(node) = ISC_FALSE;
PARENT(node) = NULL;
RIGHT(node) = NULL;
LEFT(node) = NULL;
DOWN(node) = NULL;
DATA(node) = NULL;
LOCKNUM(node) = 0;
REFS(node) = 0;
WILD(node) = 0;
DIRTY(node) = 0;
FINDCALLBACK(node) = 0;
MAKE_BLACK(node);
/*
* 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.
*
* XXX RTH
* The offsets table could be made smaller by eliminating the
* first offset, which is always 0. This requires changes to
* lib/dns/name.c.
*/
NAMELEN(node) = region.length;
PADBYTES(node) = 0;
OFFSETLEN(node) = labels;
ATTRS(node) = name->attributes;
memcpy(NAME(node), region.base, region.length);
memcpy(OFFSETS(node), name->offsets, labels);
*nodep = node;
return (ISC_R_SUCCESS);
}
static isc_result_t
join_nodes(dns_rbt_t *rbt, dns_rbtnode_t *node) {
dns_rbtnode_t *down, *newnode;
isc_result_t result;
dns_fixedname_t fixed_newname;
dns_name_t *newname, prefix, suffix;
unsigned int newlength, oldlength;
REQUIRE(VALID_RBT(rbt));
REQUIRE(node != NULL);
REQUIRE(DATA(node) == NULL && DOWN(node) != NULL);
down = DOWN(node);
dns_name_init(&prefix, NULL);
dns_name_init(&suffix, NULL);
dns_fixedname_init(&fixed_newname);
NODENAME(down, &prefix);
NODENAME(node, &suffix);
newname = dns_fixedname_name(&fixed_newname);
result = dns_name_concatenate(&prefix, &suffix, newname, NULL);
if (result != ISC_R_SUCCESS)
return (result);
/*
* 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.
*/
newlength = newname->length + newname->labels;
oldlength = NAMELEN(down) + OFFSETLEN(down);
if (newlength > oldlength + PADBYTES(down))
result = create_node(rbt->mctx, newname, &newnode);
else {
memcpy(NAME(down), newname->ndata, newname->length);
PADBYTES(down) -= newlength - oldlength;
NAMELEN(down) = newname->length;
OFFSETLEN(down) = newname->labels;
memcpy(OFFSETS(down), newname->offsets, newname->labels);
ATTRS(down) = newname->attributes;
newnode = down;
result = ISC_R_SUCCESS;
}
if (result == ISC_R_SUCCESS) {
COLOR(newnode) = COLOR(node);
PARENT(newnode) = PARENT(node);
RIGHT(newnode) = RIGHT(node);
LEFT(newnode) = LEFT(node);
IS_ROOT(newnode) = IS_ROOT(node);
DOWN(newnode) = DOWN(down);
DATA(newnode) = DATA(down);
/*
* Fix the pointers to the original node.
*/
if (IS_ROOT(node))
if (PARENT(node) == NULL)
rbt->root = newnode;
else
DOWN(PARENT(node)) = newnode;
else
if (LEFT(PARENT(node)) == node)
LEFT(PARENT(node)) = newnode;
else
RIGHT(PARENT(node)) = newnode;
if (LEFT(node) != NULL)
PARENT(LEFT(node)) = newnode;
if (RIGHT(node) != NULL)
PARENT(RIGHT(node)) = newnode;
if (DOWN(down) != NULL)
PARENT(DOWN(down)) = newnode;
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
if (newnode != down)
isc_mem_put(rbt->mctx, down, NODE_SIZE(down));
}
return (result);
}
static inline void
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
dns_rbtnode_t *child;
REQUIRE(node != NULL);
REQUIRE(rootp != NULL);
child = RIGHT(node);
INSIST(child != NULL);
RIGHT(node) = LEFT(child);
if (LEFT(child) != NULL)
PARENT(LEFT(child)) = node;
LEFT(child) = node;
if (child != NULL)
PARENT(child) = PARENT(node);
if (IS_ROOT(node)) {
*rootp = child;
IS_ROOT(child) = ISC_TRUE;
IS_ROOT(node) = ISC_FALSE;
} else {
if (LEFT(PARENT(node)) == node)
LEFT(PARENT(node)) = child;
else
RIGHT(PARENT(node)) = child;
}
PARENT(node) = child;
}
static inline void
rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
dns_rbtnode_t *child;
REQUIRE(node != NULL);
REQUIRE(rootp != NULL);
child = LEFT(node);
INSIST(child != NULL);
LEFT(node) = RIGHT(child);
if (RIGHT(child) != NULL)
PARENT(RIGHT(child)) = node;
RIGHT(child) = node;
if (child != NULL)
PARENT(child) = PARENT(node);
if (IS_ROOT(node)) {
*rootp = child;
IS_ROOT(child) = ISC_TRUE;
IS_ROOT(node) = ISC_FALSE;
} else {
if (LEFT(PARENT(node)) == node)
LEFT(PARENT(node)) = child;
else
RIGHT(PARENT(node)) = child;
}
PARENT(node) = child;
}
/*
* This is the real workhorse of the insertion code, because it does the
* true red/black tree on a single level.
*/
static void
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;
dns_name_t add_name, current_name;
dns_offsets_t add_offsets, current_offsets;
unsigned int depth;
REQUIRE(rootp != NULL);
REQUIRE(node != NULL && LEFT(node) == NULL && RIGHT(node) == NULL);
REQUIRE(current != NULL);
root = *rootp;
if (root == NULL) {
/*
* First node of a level.
*/
MAKE_BLACK(node);
IS_ROOT(node) = ISC_TRUE;
PARENT(node) = current;
*rootp = node;
return;
}
child = root;
dns_name_init(&add_name, add_offsets);
NODENAME(node, &add_name);
dns_name_init(&current_name, current_offsets);
NODENAME(current, &current_name);
if (order < 0) {
INSIST(LEFT(current) == NULL);
LEFT(current) = node;
} else {
INSIST(RIGHT(current) == NULL);
RIGHT(current) = node;
}
INSIST(PARENT(node) == NULL);
PARENT(node) = current;
MAKE_RED(node);
depth = chain->ancestor_count - 1;
while (node != root && IS_RED(chain->ancestors[depth])) {
INSIST(depth > 0);
parent = chain->ancestors[depth];
grandparent = chain->ancestors[depth - 1];
if (parent == LEFT(grandparent)) {
child = RIGHT(grandparent);
if (child != NULL && IS_RED(child)) {
MAKE_BLACK(parent);
MAKE_BLACK(child);
MAKE_RED(grandparent);
node = grandparent;
depth -= 2;
} else {
if (node == RIGHT(parent)) {
rotate_left(parent, &root);
tmp = node;
node = parent;
parent = tmp;
chain->ancestors[depth] = parent;
}
MAKE_BLACK(parent);
MAKE_RED(grandparent);
INSIST(depth > 1);
rotate_right(grandparent, &root);
}
} else {
child = LEFT(grandparent);
if (child != NULL && IS_RED(child)) {
MAKE_BLACK(parent);
MAKE_BLACK(child);
MAKE_RED(grandparent);
node = grandparent;
depth -= 2;
} else {
if (node == LEFT(parent)) {
rotate_right(parent, &root);
tmp = node;
node = parent;
parent = tmp;
chain->ancestors[depth] = parent;
}
MAKE_BLACK(parent);
MAKE_RED(grandparent);
INSIST(depth > 1);
rotate_left(grandparent, &root);
}
}
}
MAKE_BLACK(root);
ENSURE(IS_ROOT(root));
*rootp = root;
return;
}
/*
* This is the real workhorse of the deletion code, because it does the
* true red/black tree on a single level.
*/
static void
dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
dns_rbtnode_t *child, *sibling, *parent;
dns_rbtnode_t *successor;
REQUIRE(delete != NULL);
/*
* Verify that the parent history is (apparently) correct.
*/
INSIST((IS_ROOT(delete) && *rootp == delete) ||
(! IS_ROOT(delete) &&
(LEFT(PARENT(delete)) == delete ||
RIGHT(PARENT(delete)) == delete)));
child = NULL;
if (LEFT(delete) == NULL) {
if (RIGHT(delete) == NULL) {
if (IS_ROOT(delete)) {
/*
* This is the only item in the tree.
*/
*rootp = NULL;
return;
}
} else
/*
* This node has one child, on the right.
*/
child = RIGHT(delete);
} else if (RIGHT(delete) == NULL)
/*
* This node has one child, on the left.
*/
child = LEFT(delete);
else {
dns_rbtnode_t holder, *tmp = &holder;
/*
* 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.
*/
successor = RIGHT(delete);
while (LEFT(successor) != NULL)
successor = LEFT(successor);
/*
* The successor cannot possibly have a left child;
* if there is any child, it is on the right.
*/
if (RIGHT(successor) != NULL)
child = RIGHT(successor);
/* 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 be unchanged.
*/
/*
* First, put the successor in the tree location of the
* node to be deleted. Save its existing tree pointer
* information, which will be needed when linking up
* delete to the successor's old location.
*/
memcpy(tmp, successor, sizeof(dns_rbtnode_t));
if (IS_ROOT(delete)) {
*rootp = successor;
IS_ROOT(successor) = ISC_TRUE;
IS_ROOT(delete) = ISC_FALSE;
} else
if (LEFT(PARENT(delete)) == delete)
LEFT(PARENT(delete)) = successor;
else
RIGHT(PARENT(delete)) = successor;
PARENT(successor) = PARENT(delete);
LEFT(successor) = LEFT(delete);
RIGHT(successor) = RIGHT(delete);
COLOR(successor) = COLOR(delete);
if (LEFT(successor) != NULL)
PARENT(LEFT(successor)) = successor;
if (RIGHT(successor) != successor)
PARENT(RIGHT(successor)) = successor;
/*
* Now relink the node to be deleted into the
* successor's previous tree location. PARENT(tmp)
* is the successor's original parent.
*/
INSIST(! IS_ROOT(delete));
if (PARENT(tmp) == delete) {
/*
* Node being deleted was successor's parent.
*/
RIGHT(successor) = delete;
PARENT(delete) = successor;
} else {
LEFT(PARENT(tmp)) = delete;
PARENT(delete) = PARENT(tmp);
}
/*
* Original location of successor node has no left.
*/
LEFT(delete) = NULL;
RIGHT(delete) = RIGHT(tmp);
COLOR(delete) = COLOR(tmp);
}
/*
* Remove the node by removing the links from its parent.
*/
if (! IS_ROOT(delete)) {
if (LEFT(PARENT(delete)) == delete)
LEFT(PARENT(delete)) = child;
else
RIGHT(PARENT(delete)) = child;
if (child != NULL)
PARENT(child) = PARENT(delete);
} else {
/*
* This is the root being deleted, and at this point
* it is known to have just one child.
*/
*rootp = child;
IS_ROOT(child) = ISC_TRUE;
PARENT(child) = PARENT(delete);
}
/*
* Fix color violations.
*/
if (IS_BLACK(delete)) {
parent = PARENT(delete);
while (child != *rootp && IS_BLACK(child)) {
INSIST(child == NULL || ! IS_ROOT(child));
if (LEFT(parent) == child) {
sibling = RIGHT(parent);
if (IS_RED(sibling)) {
MAKE_BLACK(sibling);
MAKE_RED(parent);
rotate_left(parent, rootp);
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);
rotate_right(sibling, rootp);
sibling = RIGHT(parent);
}
COLOR(sibling) = COLOR(parent);
MAKE_BLACK(parent);
MAKE_BLACK(RIGHT(sibling));
rotate_left(parent, rootp);
child = *rootp;
}
} else {
/*
* Child is parent's right child.
* Everything is doen the same as above,
* except mirrored.
*/
sibling = LEFT(parent);
if (IS_RED(sibling)) {
MAKE_BLACK(sibling);
MAKE_RED(parent);
rotate_right(parent, rootp);
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);
rotate_left(sibling, rootp);
sibling = LEFT(parent);
}
COLOR(sibling) = COLOR(parent);
MAKE_BLACK(parent);
MAKE_BLACK(LEFT(sibling));
rotate_right(parent, rootp);
child = *rootp;
}
}
parent = PARENT(child);
}
if (IS_RED(child))
MAKE_BLACK(child);
}
}
/*
* This should only be used on the root of a tree, because no color fixup
* is done at all.
*
* NOTE: No root pointer maintenance is done, because the function is only
* used for two cases:
* + deleting everything DOWN from a node that is itself being deleted, and
* + deleting the entire tree of trees from dns_rbt_destroy.
* In each case, the root pointer is no longer relevant, so there
* is no need for a root parameter to this function.
*
* If the function is ever intended to be used to delete something where
* a pointer needs to be told that this tree no longer exists,
* this function would need to adjusted accordingly.
*/
static void
dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
REQUIRE(VALID_RBT(rbt));
if (node == NULL)
return;
if (LEFT(node) != NULL)
dns_rbt_deletetree(rbt, LEFT(node));
if (RIGHT(node) != NULL)
dns_rbt_deletetree(rbt, RIGHT(node));
if (DOWN(node) != NULL)
dns_rbt_deletetree(rbt, DOWN(node));
if (DATA(node) != NULL && rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node), rbt->deleter_arg);
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
}
static void
dns_rbt_indent(int depth) {
int i;
for (i = 0; i < depth; i++)
putchar('\t');
}
static void
dns_rbt_printnodename(dns_rbtnode_t *node) {
isc_buffer_t target;
isc_region_t r;
dns_name_t name;
char buffer[1024];
dns_offsets_t offsets;
r.length = NAMELEN(node);
r.base = NAME(node);
dns_name_init(&name, offsets);
dns_name_fromregion(&name, &r);
isc_buffer_init(&target, buffer, 255);
/*
* ISC_FALSE means absolute names have the final dot added.
*/
dns_name_totext(&name, ISC_FALSE, &target);
printf("%.*s", (int)target.used, (char *)target.base);
}
static void
dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent, int depth) {
dns_rbt_indent(depth);
if (root != NULL) {
dns_rbt_printnodename(root);
printf(" (%s", IS_RED(root) ? "RED" : "black");
if (parent) {
printf(" from ");
dns_rbt_printnodename(parent);
}
if ((! IS_ROOT(root) && PARENT(root) != parent) ||
( IS_ROOT(root) && depth > 0 &&
DOWN(PARENT(root)) != root)) {
printf(" (BAD parent pointer! -> ");
if (PARENT(root) != NULL)
dns_rbt_printnodename(PARENT(root));
else
printf("NULL");
printf(")");
}
printf(")\n");
depth++;
if (DOWN(root)) {
dns_rbt_indent(depth);
printf("++ BEG down from ");
dns_rbt_printnodename(root);
printf("\n");
dns_rbt_printtree(DOWN(root), NULL, depth);
dns_rbt_indent(depth);
printf("-- END down from ");
dns_rbt_printnodename(root);
printf("\n");
}
if (IS_RED(root) && IS_RED(LEFT(root)))
printf("** Red/Red color violation on left\n");
dns_rbt_printtree(LEFT(root), root, depth);
if (IS_RED(root) && IS_RED(RIGHT(root)))
printf("** Red/Red color violation on right\n");
dns_rbt_printtree(RIGHT(root), root, depth);
} else
printf("NULL\n");
}
void
dns_rbt_printall(dns_rbt_t *rbt) {
REQUIRE(VALID_RBT(rbt));
dns_rbt_printtree(rbt->root, NULL, 0);
}
/*
* Chain Functions
*/
void
dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
/*
* Initialize 'chain'.
*/
REQUIRE(chain != NULL);
chain->mctx = mctx;
chain->end = NULL;
chain->ancestors = chain->ancestor_block;
chain->ancestor_count = 0;
chain->ancestor_maxitems = DNS_RBT_ANCESTORBLOCK;
chain->level_count = 0;
chain->level_matches = 0;
chain->magic = CHAIN_MAGIC;
}
isc_result_t
dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
dns_name_t *origin, dns_rbtnode_t **node)
{
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_CHAIN(chain));
if (node != NULL)
*node = chain->end;
if (chain->end == NULL)
return (ISC_R_NOTFOUND);
if (name != NULL) {
NODENAME(chain->end, name);
if (chain->level_count == 0) {
/*
* Names in the top level tree are all absolute.
* Always make 'name' relative.
*/
INSIST(dns_name_isabsolute(name));
/*
* This is cheaper than dns_name_getlabelsequence().
*/
name->labels--;
name->length--;
name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
}
}
if (origin != NULL) {
if (chain->level_count > 0)
result = chain_name(chain, origin, ISC_FALSE);
else
result = dns_name_concatenate(NULL, dns_rootname,
origin, NULL);
}
return (result);
}
isc_result_t
dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
dns_name_t *origin)
{
dns_rbtnode_t *current, *previous, *predecessor;
isc_result_t result = ISC_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) {
INSIST(chain->ancestor_count > 1);
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 */
if (origin != NULL)
new_origin = ISC_TRUE;
}
} else if (chain->level_count > 0) {
/*
* Got to the root of this level without having traversed
* any right links. Ascend the tree one level.
*/
INSIST(chain->level_count > 0 &&
chain->ancestor_count > 0);
predecessor = chain->levels[--chain->level_count];
chain->ancestor_count--;
/* XXX DCL probably needs work on the concept */
/*
* Don't declare an origin change when the new origin is "."
* at the top level tree, because "." is declared as the origin
* for the second level tree.
*/
if (origin != NULL &&
(chain->level_count > 0 || OFFSETLEN(predecessor) > 1))
new_origin = ISC_TRUE;
}
if (predecessor != NULL) {
chain->end = predecessor;
if (new_origin) {
result = dns_rbtnodechain_current(chain, name, origin,
NULL);
if (result == ISC_R_SUCCESS)
result = DNS_R_NEWORIGIN;
} else
result = dns_rbtnodechain_current(chain, name, NULL,
NULL);
} else
result = ISC_R_NOMORE;
return (result);
}
isc_result_t
dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
dns_name_t *origin)
{
dns_rbtnode_t *current, *previous, *successor;
isc_result_t result = ISC_R_SUCCESS;
isc_boolean_t new_origin = ISC_FALSE;
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
successor = NULL;
current = chain->end;
if (DOWN(current) != NULL) {
/*
* Don't declare an origin change when the new origin is "."
* at the second level tree, because "." is already declared
* as the origin for the top level tree.
*/
if (chain->level_count > 0 ||
OFFSETLEN(current) > 1)
new_origin = ISC_TRUE;
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) {
/*
* The successor is up, either in this level or a previous one.
* Head back toward the root of the tree, looking for any path
* that was via a left link; the successor is the node that has
* that left link. In the event the root of the level is
* reached without having traversed any left links, ascend one
* level and look for either a right link off the point of
* ascent, or search for a left link upward again, repeating
* ascents until either case is true.
*/
do {
while (chain->ancestors[chain->ancestor_count - 1] !=
NULL) {
INSIST(chain->ancestor_count > 1);
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.
*/
if (chain->level_count == 0)
break;
INSIST(chain->ancestor_count > 0);
current = chain->levels[--chain->level_count];
chain->ancestor_count--;
new_origin = ISC_TRUE;
if (RIGHT(current) != NULL)
break;
}
} while (successor == NULL);
}
if (successor == NULL && RIGHT(current) != NULL) {
ADD_ANCESTOR(chain, current);
current = RIGHT(current);
while (LEFT(current) != NULL) {
ADD_ANCESTOR(chain, current);
current = LEFT(current);
}
successor = current;
}
if (successor != NULL) {
chain->end = successor;
/*
* It is not necessary to use dns_rbtnodechain_current like
* the other functions because this function will never
* find a node in the topmost level. This is because the
* root level will never be more than one name, and everything
* in the megatree is a successor to that node, down at
* the second level or below.
*/
if (name != NULL)
NODENAME(chain->end, name);
if (new_origin) {
if (origin != NULL)
result = chain_name(chain, origin, ISC_FALSE);
if (result == ISC_R_SUCCESS)
result = DNS_R_NEWORIGIN;
} else
result = ISC_R_SUCCESS;
} else
result = ISC_R_NOMORE;
return (result);
}
isc_result_t
dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
dns_name_t *name, dns_name_t *origin)
{
isc_result_t result;
REQUIRE(VALID_RBT(rbt));
REQUIRE(VALID_CHAIN(chain));
dns_rbtnodechain_reset(chain);
ADD_ANCESTOR(chain, NULL);
chain->end = rbt->root;
result = dns_rbtnodechain_current(chain, name, origin, NULL);
if (result == ISC_R_SUCCESS)
result = DNS_R_NEWORIGIN;
return (result);
}
isc_result_t
dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
dns_name_t *name, dns_name_t *origin)
{
isc_result_t result;
REQUIRE(VALID_RBT(rbt));
REQUIRE(VALID_CHAIN(chain));
dns_rbtnodechain_reset(chain);
ADD_ANCESTOR(chain, NULL);
result = move_chain_to_last(chain, rbt->root);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_rbtnodechain_current(chain, name, origin, NULL);
if (result == ISC_R_SUCCESS)
result = DNS_R_NEWORIGIN;
return (result);
}
void
dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
/*
* Free any dynamic storage associated with 'chain', and then
* reinitialize 'chain'.
*/
REQUIRE(VALID_CHAIN(chain));
put_ancestor_mem(chain);
chain->end = NULL;
chain->ancestors = chain->ancestor_block;
chain->ancestor_count = 0;
chain->ancestor_maxitems = DNS_RBT_ANCESTORBLOCK;
chain->level_count = 0;
chain->level_matches = 0;
}
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;
}