#define RIGHT(node) ((node)->right)
#define DOWN(node) ((node)->down)
#define DATA(node) ((node)->data)
+#define IS_EMPTY(node) ((node)->data == NULL)
#define HASHNEXT(node) ((node)->hashnext)
#define HASHVAL(node) ((node)->hashval)
#define COLOR(node) ((node)->color)
name->attributes |= DNS_NAMEATTR_READONLY;
}
+void
+dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name) {
+ 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;
+}
+
+dns_rbtnode_t *
+dns_rbt_root(dns_rbt_t *rbt) {
+ return rbt->root;
+}
+
#ifdef DNS_RBT_USEHASH
static isc_result_t
inithash(dns_rbt_t *rbt);
return (name);
}
-static void printnodename(dns_rbtnode_t *node);
-
static void
hexdump(const char *desc, unsigned char *data, size_t size) {
char hexdump[BUFSIZ * 2 + 1];
} while (size > 0);
fprintf(stderr, "\n");
}
-#endif
+#endif /* DEBUG */
+
+/* The passed node must not be NULL. */
+static inline dns_rbtnode_t *
+get_subtree_root(dns_rbtnode_t *node) {
+ while (!IS_ROOT(node)) {
+ node = PARENT(node);
+ }
+
+ return (node);
+}
+/* Upper node is the parent of the root of the passed node's
+ * subtree. The passed node must not be NULL.
+ */
static inline dns_rbtnode_t *
-find_up(dns_rbtnode_t *node) {
- dns_rbtnode_t *root;
+get_upper_node(dns_rbtnode_t *node) {
+ dns_rbtnode_t *root = get_subtree_root(node);
/*
* Return the node in the level above the argument node that points
* to the level the argument node is in. If the argument node is in
* the top level, the return value is NULL.
*/
- for (root = node; ! IS_ROOT(root); root = PARENT(root))
- ; /* Nothing. */
-
return (PARENT(root));
}
+size_t
+dns__rbtnode_getdistance(dns_rbtnode_t *node) {
+ size_t nodes = 1;
+
+ while (node != NULL) {
+ if (IS_ROOT(node))
+ break;
+ nodes++;
+ node = PARENT(node);
+ }
+
+ return (nodes);
+}
+
/*
* Forward declarations.
*/
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep);
static void
-printnodename(dns_rbtnode_t *node);
+printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f);
static void
freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
if (rbt->root == NULL)
return (ISC_R_NOTFOUND);
- else {
- /*
- * Appease GCC about variables it incorrectly thinks are
- * possibly used uninitialized.
- */
- compared = dns_namereln_none;
- last_compared = NULL;
- order = 0;
- }
+
+ /*
+ * Appease GCC about variables it incorrectly thinks are
+ * possibly used uninitialized.
+ */
+ compared = dns_namereln_none;
+ last_compared = NULL;
+ order = 0;
dns_fixedname_init(&fixedcallbackname);
callback_name = dns_fixedname_name(&fixedcallbackname);
NODENAME(current, ¤t_name);
compared = dns_name_fullcompare(search_name, ¤t_name,
&order, &common_labels);
+ /*
+ * last_compared is used as a shortcut to start (or
+ * continue rather) finding the stop-node of the search
+ * when hashing was used (see much below in this
+ * function).
+ */
last_compared = current;
if (compared == dns_namereln_equal)
if (compared == dns_namereln_none) {
#ifdef DNS_RBT_USEHASH
+ /*
+ * Here, current is pointing at a subtree root
+ * node. We try to find a matching node using
+ * the hashtable. We can get one of 3 results
+ * here: (a) we locate the matching node, (b) we
+ * find a node to which the current node has a
+ * subdomain relation, (c) we fail to find (a)
+ * or (b).
+ */
+
dns_name_t hash_name;
dns_rbtnode_t *hnode;
dns_rbtnode_t *up_current;
hashagain:
/*
- * Hash includes tail.
+ * Compute the hash over the full absolute
+ * name. Look for the smallest suffix match at
+ * this tree level (hlevel), and then at every
+ * iteration, look for the next smallest suffix
+ * match (add another subdomain label to the
+ * absolute name being hashed).
*/
dns_name_getlabelsequence(name,
nlabels - tlabels,
nlabels - tlabels,
tlabels, &hash_name);
+ /*
+ * Walk all the nodes in the hash bucket pointed
+ * by the computed hash value.
+ */
for (hnode = rbt->hashtable[hash % rbt->hashsize];
hnode != NULL;
hnode = hnode->hashnext)
if (hash != HASHVAL(hnode))
continue;
- if (find_up(hnode) != up_current)
+ /*
+ * This checks that the hashed label
+ * sequence being looked up is at the
+ * same tree level, so that we don't
+ * match a labelsequence from some other
+ * subdomain.
+ */
+ if (get_upper_node(hnode) != up_current)
continue;
+
dns_name_init(&hnode_name, NULL);
NODENAME(hnode, &hnode_name);
if (dns_name_equal(&hnode_name, &hash_name))
* deleted. If the deleted node is the top level, parent will be set
* to NULL.
*/
- parent = find_up(node);
+ parent = get_upper_node(node);
/*
* This node now has no down pointer (either because it didn't
if (result != ISC_R_SUCCESS)
break;
- node = find_up(node);
+ node = get_upper_node(node);
} while (! dns_name_isabsolute(name));
return (result);
goto again;
}
+static size_t
+getheight_helper(dns_rbtnode_t *node) {
+ size_t dl, dr;
+ size_t this_height, down_height;
+
+ if (node == NULL)
+ return (0);
+
+ dl = getheight_helper(LEFT(node));
+ dr = getheight_helper(RIGHT(node));
+
+ this_height = ISC_MAX(dl + 1, dr + 1);
+ down_height = getheight_helper(DOWN(node));
+
+ return (ISC_MAX(this_height, down_height));
+}
+
+size_t
+dns__rbt_getheight(dns_rbt_t *rbt) {
+ return (getheight_helper(rbt->root));
+}
+
+static isc_boolean_t
+check_properties_helper(dns_rbtnode_t *node) {
+ if (node == NULL)
+ return (ISC_TRUE);
+
+ if (IS_RED(node)) {
+ /* Root nodes must be BLACK. */
+ if (IS_ROOT(node))
+ return (ISC_FALSE);
+
+ /* Both children of RED nodes must be BLACK. */
+ if (IS_RED(LEFT(node)) || IS_RED(RIGHT(node)))
+ return (ISC_FALSE);
+ }
+
+ /* If node is assigned to the down_ pointer of its parent, it is
+ * a subtree root and must have the flag set.
+ */
+ if (((!PARENT(node)) ||
+ (DOWN(PARENT(node)) == node)) &&
+ (!IS_ROOT(node)))
+ {
+ return (ISC_FALSE);
+ }
+
+ /* Repeat tests with this node's children. */
+ return (check_properties_helper(LEFT(node)) &&
+ check_properties_helper(RIGHT(node)) &&
+ check_properties_helper(DOWN(node)));
+}
+
+static isc_boolean_t
+check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) {
+ size_t dl, dr, dd;
+
+ if (node == NULL) {
+ *distance = 1;
+ return (ISC_TRUE);
+ }
+
+ if (!check_black_distance_helper(LEFT(node), &dl))
+ return (ISC_FALSE);
+
+ if (!check_black_distance_helper(RIGHT(node), &dr))
+ return (ISC_FALSE);
+
+ if (!check_black_distance_helper(DOWN(node), &dd))
+ return (ISC_FALSE);
+
+ /* Left and right side black node counts must match. */
+ if (dl != dr)
+ return (ISC_FALSE);
+
+ if (IS_BLACK(node))
+ dl++;
+
+ *distance = dl;
+
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns__rbt_checkproperties(dns_rbt_t *rbt) {
+ size_t dd;
+
+ if (!check_properties_helper(rbt->root))
+ return (ISC_FALSE);
+
+ /* Path from a given node to all its leaves must contain the
+ * same number of BLACK child nodes. This is done separately
+ * here instead of inside check_properties_helper() as
+ * it would take (n log n) complexity otherwise.
+ */
+ return (check_black_distance_helper(rbt->root, &dd));
+}
+
static void
-dns_rbt_indent(int depth) {
+dns_rbt_indent(FILE *f, int depth) {
int i;
- printf("%4d ", depth);
+ fprintf(f, "%4d ", depth);
for (i = 0; i < depth; i++)
- printf("- ");
+ fprintf(f, "- ");
}
void
-dns_rbt_printnodeinfo(dns_rbtnode_t *n) {
- printf("Node info for nodename: ");
- printnodename(n);
- printf("\n");
+dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) {
+ fprintf(f, "Node info for nodename: ");
+ printnodename(n, ISC_TRUE, f);
+ fprintf(f, "\n");
- printf("n = %p\n", n);
+ fprintf(f, "n = %p\n", n);
- printf("Relative pointers: %s%s%s%s%s\n",
+ fprintf(f, "Relative pointers: %s%s%s%s%s\n",
(n->parent_is_relative == 1 ? " P" : ""),
(n->right_is_relative == 1 ? " R" : ""),
(n->left_is_relative == 1 ? " L" : ""),
(n->down_is_relative == 1 ? " D" : ""),
(n->data_is_relative == 1 ? " T" : ""));
- printf("node lock address = %d\n", n->locknum);
+ fprintf(f, "node lock address = %d\n", n->locknum);
- printf("Parent: %p\n", n->parent);
- printf("Right: %p\n", n->right);
- printf("Left: %p\n", n->left);
- printf("Down: %p\n", n->down);
- printf("daTa: %p\n", n->data);
+ fprintf(f, "Parent: %p\n", n->parent);
+ fprintf(f, "Right: %p\n", n->right);
+ fprintf(f, "Left: %p\n", n->left);
+ fprintf(f, "Down: %p\n", n->down);
+ fprintf(f, "daTa: %p\n", n->data);
}
static void
-printnodename(dns_rbtnode_t *node) {
+printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f) {
isc_region_t r;
dns_name_t name;
char buffer[DNS_NAME_FORMATSIZE];
dns_name_format(&name, buffer, sizeof(buffer));
- printf("\"%s\"", buffer);
+ if (quoted)
+ fprintf(f, "\"%s\"", buffer);
+ else
+ fprintf(f, "%s", buffer);
}
static void
-dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent,
+print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent,
int depth, const char *direction,
- void (*data_printer)(FILE *, void *))
+ void (*data_printer)(FILE *, void *), FILE *f)
{
- dns_rbt_indent(depth);
+ dns_rbt_indent(f, depth);
if (root != NULL) {
- printnodename(root);
- printf(" (%s, %s", direction, IS_RED(root) ? "RED" : "BLACK");
+ printnodename(root, ISC_TRUE, f);
+ fprintf(f, " (%s, %s", direction,
+ IS_RED(root) ? "RED" : "BLACK");
if ((! IS_ROOT(root) && PARENT(root) != parent) ||
( IS_ROOT(root) && depth > 0 &&
DOWN(PARENT(root)) != root)) {
- printf(" (BAD parent pointer! -> ");
+ fprintf(f, " (BAD parent pointer! -> ");
if (PARENT(root) != NULL)
- printnodename(PARENT(root));
+ printnodename(PARENT(root), ISC_TRUE, f);
else
- printf("NULL");
- printf(")");
+ fprintf(f, "NULL");
+ fprintf(f, ")");
}
- printf(")");
+ fprintf(f, ")");
if (root->data != NULL && data_printer != NULL) {
- printf(" data@%p: ", root->data);
- data_printer(stdout, root->data);
+ fprintf(f, " data@%p: ", root->data);
+ data_printer(f, root->data);
}
- printf("\n");
+ fprintf(f, "\n");
depth++;
if (IS_RED(root) && IS_RED(LEFT(root)))
- printf("** Red/Red color violation on left\n");
- dns_rbt_printtree(LEFT(root), root, depth, "left",
- data_printer);
+ fprintf(f, "** Red/Red color violation on left\n");
+ print_text_helper(LEFT(root), root, depth, "left",
+ data_printer, f);
if (IS_RED(root) && IS_RED(RIGHT(root)))
- printf("** Red/Red color violation on right\n");
- dns_rbt_printtree(RIGHT(root), root, depth, "right",
- data_printer);
+ fprintf(f, "** Red/Red color violation on right\n");
+ print_text_helper(RIGHT(root), root, depth, "right",
+ data_printer, f);
- dns_rbt_printtree(DOWN(root), NULL, depth, "down",
- data_printer);
+ print_text_helper(DOWN(root), NULL, depth, "down",
+ data_printer, f);
} else {
- printf("NULL (%s)\n", direction);
+ fprintf(f, "NULL (%s)\n", direction);
}
}
void
-dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
+dns_rbt_printtext(dns_rbt_t *rbt,
+ void (*data_printer)(FILE *, void *), FILE *f)
+{
+ REQUIRE(VALID_RBT(rbt));
+
+ print_text_helper(rbt->root, NULL, 0, "root", data_printer, f);
+}
+
+static int
+print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount,
+ isc_boolean_t show_pointers, FILE *f)
+{
+ unsigned int l, r, d;
+
+ if (node == NULL)
+ return (0);
+
+ l = print_dot_helper(LEFT(node), nodecount, show_pointers, f);
+ r = print_dot_helper(RIGHT(node), nodecount, show_pointers, f);
+ d = print_dot_helper(DOWN(node), nodecount, show_pointers, f);
+
+ *nodecount += 1;
+
+ fprintf(f, "node%u[label = \"<f0> |<f1> ", *nodecount);
+ printnodename(node, ISC_FALSE, f);
+ fprintf(f, "|<f2>");
+
+ if (show_pointers)
+ fprintf(f, "|<f3> n=%p|<f4> p=%p", node, PARENT(node));
+
+ fprintf(f, "\"] [");
+
+ if (IS_RED(node))
+ fprintf(f, "color=red");
+ else
+ fprintf(f, "color=black");
+
+ /* XXXMUKS: verify that IS_ROOT() indicates subtree root and not
+ * forest root.
+ */
+ if (IS_ROOT(node))
+ fprintf(f, ",penwidth=3");
+
+ if (IS_EMPTY(node))
+ fprintf(f, ",style=filled,fillcolor=lightgrey");
+
+ fprintf(f, "];\n");
+
+ if (LEFT(node) != NULL)
+ fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l);
+
+ if (DOWN(node) != NULL)
+ fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n",
+ *nodecount, d);
+
+ if (RIGHT(node) != NULL)
+ fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r);
+
+ return (*nodecount);
+}
+
+void
+dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f) {
+ unsigned int nodecount = 0;
REQUIRE(VALID_RBT(rbt));
- dns_rbt_printtree(rbt->root, NULL, 0, "root", data_printer);
+ fprintf(f, "digraph g {\n");
+ fprintf(f, "node [shape = record,height=.1];\n");
+ print_dot_helper(rbt->root, &nodecount, show_pointers, f);
+ fprintf(f, "}\n");
}
/*
chain->magic = 0;
}
+
+/* XXXMUKS:
+ *
+ * - worth removing inline as static functions are inlined automatically
+ * where suitable by modern compilers.
+ * - bump the size of dns_rbt.nodecount to size_t.
+ * - the dumpfile header also contains a nodecount that is unsigned
+ * int. If large files (> 2^32 nodes) are to be supported, the
+ * allocation for this field should be increased.
+ */
--- /dev/null
+/*
+ * Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: rbt_test.c,v 1.1.14.8 2012/02/10 16:24:37 ckb Exp $ */
+
+/* ! \file */
+
+#include <config.h>
+#include <atf-c.h>
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h> /* uintptr_t */
+#endif
+
+#include <dns/rbt.h>
+#include <dns/fixedname.h>
+#include <dns/result.h>
+#include <dns/compress.h>
+#include "dnstest.h"
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/string.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+typedef struct data_holder {
+ int len;
+ const char *data;
+} data_holder_t;
+
+typedef struct rbt_testdata {
+ const char *name;
+ size_t name_len;
+ data_holder_t data;
+} rbt_testdata_t;
+
+#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
+
+rbt_testdata_t testdata[] = {
+ DATA_ITEM("first.com."),
+ DATA_ITEM("one.net."),
+ DATA_ITEM("two.com."),
+ DATA_ITEM("three.org."),
+ DATA_ITEM("asdf.com."),
+ DATA_ITEM("ghjkl.com."),
+ DATA_ITEM("1.edu."),
+ DATA_ITEM("2.edu."),
+ DATA_ITEM("3.edu."),
+ DATA_ITEM("123.edu."),
+ DATA_ITEM("1236.com."),
+ DATA_ITEM("and_so_forth.com."),
+ DATA_ITEM("thisisalongname.com."),
+ DATA_ITEM("a.b."),
+ DATA_ITEM("test.net."),
+ DATA_ITEM("whoknows.org."),
+ DATA_ITEM("blargh.com."),
+ DATA_ITEM("www.joe.com."),
+ DATA_ITEM("test.com."),
+ DATA_ITEM("isc.org."),
+ DATA_ITEM("uiop.mil."),
+ DATA_ITEM("last.fm."),
+ { NULL, 0, { 0, NULL } }
+};
+
+static void
+delete_data(void *data, void *arg) {
+ UNUSED(arg);
+ UNUSED(data);
+}
+
+static isc_result_t
+write_data(FILE *file, unsigned char *datap, void *arg, isc_uint64_t *crc) {
+ isc_result_t result;
+ size_t ret = 0;
+ data_holder_t *data = (data_holder_t *)datap;
+ data_holder_t temp;
+ off_t where;
+
+ UNUSED(arg);
+
+ REQUIRE(file != NULL);
+ REQUIRE(crc != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE((data->len == 0 && data->data == NULL) ||
+ (data->len != 0 && data->data != NULL));
+
+ result = isc_stdio_tell(file, &where);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ temp = *data;
+ temp.data = (data->len == 0
+ ? NULL
+ : (char *)((uintptr_t)where + sizeof(data_holder_t)));
+
+ isc_crc64_update(crc, (void *)&temp, sizeof(temp));
+ ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
+ if (ret != 1)
+ return (ISC_R_FAILURE);
+ if (data->len > 0) {
+ isc_crc64_update(crc, (const void *)data->data, data->len);
+ ret = fwrite(data->data, data->len, 1, file);
+ if (ret != 1)
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
+ isc_uint64_t *crc)
+{
+ data_holder_t *data = p->data;
+ size_t size;
+
+ UNUSED(base);
+ UNUSED(max);
+ UNUSED(arg);
+
+ REQUIRE(crc != NULL);
+ REQUIRE(p != NULL);
+
+
+ if (data == NULL)
+ printf("fixing data: data NULL\n");
+ else
+ printf("fixing data: len %d, data %p\n", data->len, data->data);
+
+ if (data == NULL ||
+ (data->len == 0 && data->data != NULL) ||
+ (data->len != 0 && data->data == NULL))
+ return (ISC_R_INVALIDFILE);
+
+ size = max - ((char *)p - (char *)base);
+
+ if (data->len > (int) size || data->data > (const char *) max) {
+ printf("data invalid\n");
+ return (ISC_R_INVALIDFILE);
+ }
+
+ isc_crc64_update(crc, (void *)data, sizeof(*data));
+
+ data->data = (data->len == 0)
+ ? NULL
+ : (char *)data + sizeof(data_holder_t);
+
+ if (data->len > 0)
+ isc_crc64_update(crc, (const void *)data->data, data->len);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Load test data into the RBT.
+ */
+static void
+add_test_data(isc_mem_t *mctx, dns_rbt_t *rbt) {
+ char buffer[1024];
+ isc_buffer_t b;
+ isc_result_t result;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ dns_compress_t cctx;
+ rbt_testdata_t *testdatap = testdata;
+
+ dns_compress_init(&cctx, -1, mctx);
+
+ while (testdatap->name != NULL && testdatap->data.data != NULL) {
+ memmove(buffer, testdatap->name, testdatap->name_len);
+
+ isc_buffer_init(&b, buffer, testdatap->name_len);
+ isc_buffer_add(&b, testdatap->name_len);
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ testdatap++;
+ continue;
+ }
+
+ if (name != NULL) {
+ result = dns_rbt_addname(rbt, name, &testdatap->data);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ }
+ testdatap++;
+ }
+
+ dns_compress_invalidate(&cctx);
+}
+
+/*
+ * Walk the tree and ensure that all the test nodes are present.
+ */
+static void
+check_test_data(dns_rbt_t *rbt) {
+ char buffer[1024];
+ char *arg;
+ dns_fixedname_t fname;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ data_holder_t *data;
+ isc_result_t result;
+ dns_name_t *foundname;
+ rbt_testdata_t *testdatap = testdata;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ while (testdatap->name != NULL && testdatap->data.data != NULL) {
+ memmove(buffer, testdatap->name, testdatap->name_len + 1);
+ arg = buffer;
+
+ isc_buffer_init(&b, arg, testdatap->name_len);
+ isc_buffer_add(&b, testdatap->name_len);
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ testdatap++;
+ continue;
+ }
+
+ data = NULL;
+ result = dns_rbt_findname(rbt, name, 0, foundname,
+ (void *) &data);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+ testdatap++;
+ }
+}
+
+static void
+data_printer(FILE *out, void *datap)
+{
+ data_holder_t *data = (data_holder_t *)datap;
+
+ fprintf(out, "%d bytes, %s", data->len, data->data);
+}
+
+ATF_TC(serialize);
+ATF_TC_HEAD(serialize, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
+}
+ATF_TC_BODY(serialize, tc) {
+ dns_rbt_t *rbt = NULL;
+ isc_result_t result;
+ FILE *rbtfile = NULL;
+ dns_rbt_t *rbt_deserialized = NULL;
+ off_t offset;
+ int fd;
+ off_t filesize = 0;
+ char *base;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+ add_test_data(mctx, rbt);
+
+ dns_rbt_printtext(rbt, data_printer, stdout);
+
+ /*
+ * Serialize the tree.
+ */
+ printf("serialization begins.\n");
+ rbtfile = fopen("./zone.bin", "w+b");
+ ATF_REQUIRE(rbtfile != NULL);
+ result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+ &offset);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ dns_rbt_destroy(&rbt);
+
+ /*
+ * Deserialize the tree
+ */
+ printf("deserialization begins.\n");
+
+ /*
+ * Map in the whole file in one go
+ */
+ fd = open("zone.bin", O_RDWR);
+ isc_file_getsizefd(fd, &filesize);
+ base = mmap(NULL, filesize,
+ PROT_READ|PROT_WRITE,
+ MAP_FILE|MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+ close(fd);
+
+ result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+ delete_data, NULL, fix_data, NULL,
+ NULL, &rbt_deserialized);
+
+ /* Test to make sure we have a valid tree */
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ if (rbt_deserialized == NULL)
+ atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
+
+ check_test_data(rbt_deserialized);
+
+ dns_rbt_printtext(rbt_deserialized, data_printer, stdout);
+
+ dns_rbt_destroy(&rbt_deserialized);
+ munmap(base, filesize);
+ unlink("zone.bin");
+ dns_test_end();
+}
+
+ATF_TC(deserialize_corrupt);
+ATF_TC_HEAD(deserialize_corrupt, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
+}
+ATF_TC_BODY(deserialize_corrupt, tc) {
+ dns_rbt_t *rbt = NULL;
+ isc_result_t result;
+ FILE *rbtfile = NULL;
+ off_t offset;
+ int fd;
+ off_t filesize = 0;
+ char *base, *p, *q;
+ isc_uint32_t r;
+ int i;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Set up map file */
+ result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ add_test_data(mctx, rbt);
+ rbtfile = fopen("./zone.bin", "w+b");
+ ATF_REQUIRE(rbtfile != NULL);
+ result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+ &offset);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ dns_rbt_destroy(&rbt);
+
+ /* Read back with random fuzzing */
+ for (i = 0; i < 256; i++) {
+ dns_rbt_t *rbt_deserialized = NULL;
+
+ fd = open("zone.bin", O_RDWR);
+ isc_file_getsizefd(fd, &filesize);
+ base = mmap(NULL, filesize,
+ PROT_READ|PROT_WRITE,
+ MAP_FILE|MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+ close(fd);
+
+ /* Randomly fuzz a portion of the memory */
+ isc_random_get(&r);
+ p = base + (r % filesize);
+ q = base + filesize;
+ isc_random_get(&r);
+ q -= (r % (q - p));
+ while (p++ < q) {
+ isc_random_get(&r);
+ *p = r & 0xff;
+ }
+
+ result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+ delete_data, NULL,
+ fix_data, NULL,
+ NULL, &rbt_deserialized);
+ printf("%d: %s\n", i, isc_result_totext(result));
+
+ /* Test to make sure we have a valid tree */
+ ATF_REQUIRE(result == ISC_R_SUCCESS ||
+ result == ISC_R_INVALIDFILE);
+ if (result != ISC_R_SUCCESS)
+ ATF_REQUIRE(rbt_deserialized == NULL);
+
+ if (rbt_deserialized != NULL)
+ dns_rbt_destroy(&rbt_deserialized);
+
+ munmap(base, filesize);
+ }
+
+ unlink("zone.bin");
+ dns_test_end();
+}
+
+
+ATF_TC(serialize_align);
+ATF_TC_HEAD(serialize_align, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test the dns_rbt_serialize_align() function.");
+}
+ATF_TC_BODY(serialize_align, tc) {
+ UNUSED(tc);
+
+ ATF_CHECK(dns_rbt_serialize_align(0) == 0);
+ ATF_CHECK(dns_rbt_serialize_align(1) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(2) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(3) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(4) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(5) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(6) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(7) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(8) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(9) == 16);
+ ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
+ ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, serialize);
+ ATF_TP_ADD_TC(tp, deserialize_corrupt);
+ ATF_TP_ADD_TC(tp, serialize_align);
+
+ return (atf_no_error());
+}
#include <isc/string.h>
#include <fcntl.h>
#include <unistd.h>
-#include <sys/mman.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h> /* uintptr_t */
#include <isc/task.h>
#include <isc/timer.h>
#include <isc/util.h>
+#include <isc/print.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dst/dst.h>
-#ifndef MAP_FILE
-#define MAP_FILE 0
-#endif
+#include <ctype.h>
+
+typedef struct {
+ dns_rbt_t *rbt;
+ dns_rbt_t *rbt_distances;
+} test_context_t;
+
+/* The initial structure of domain tree will be as follows:
+ *
+ * .
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
-typedef struct data_holder {
- int len;
- const char *data;
-} data_holder_t;
-
-typedef struct rbt_testdata {
- const char *name;
- size_t name_len;
- data_holder_t data;
-} rbt_testdata_t;
-
-#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
-
-rbt_testdata_t testdata[] = {
- DATA_ITEM("first.com."),
- DATA_ITEM("one.net."),
- DATA_ITEM("two.com."),
- DATA_ITEM("three.org."),
- DATA_ITEM("asdf.com."),
- DATA_ITEM("ghjkl.com."),
- DATA_ITEM("1.edu."),
- DATA_ITEM("2.edu."),
- DATA_ITEM("3.edu."),
- DATA_ITEM("123.edu."),
- DATA_ITEM("1236.com."),
- DATA_ITEM("and_so_forth.com."),
- DATA_ITEM("thisisalongname.com."),
- DATA_ITEM("a.b."),
- DATA_ITEM("test.net."),
- DATA_ITEM("whoknows.org."),
- DATA_ITEM("blargh.com."),
- DATA_ITEM("www.joe.com."),
- DATA_ITEM("test.com."),
- DATA_ITEM("isc.org."),
- DATA_ITEM("uiop.mil."),
- DATA_ITEM("last.fm."),
- { NULL, 0, { 0, NULL } }
+/* The full absolute names of the nodes in the tree (the tree also
+ * contains "." which is not included in this list).
+ */
+static const char * const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"
};
+static const size_t domain_names_count = (sizeof(domain_names) /
+ sizeof(domain_names[0]));
+
+/* These are set as the node data for the tree used in distances check
+ * (for the names in domain_names[] above).
+ */
+static const int node_distances[] = {
+ 3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
+};
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ * . (no data, can't be found)
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+static const char * const ordered_names[] = {
+ "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+ "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+ "g.h", "i.g.h", "k.g.h"};
+
+static const size_t ordered_names_count = (sizeof(ordered_names) /
+ sizeof(*ordered_names));
+
static void
delete_data(void *data, void *arg) {
UNUSED(arg);
- UNUSED(data);
+
+ isc_mem_put(mctx, data, sizeof(size_t));
}
-static isc_result_t
-write_data(FILE *file, unsigned char *datap, void *arg, isc_uint64_t *crc) {
+static void
+build_name_from_str(const char *namestr, dns_fixedname_t *fname) {
+ size_t length;
+ isc_buffer_t *b = NULL;
isc_result_t result;
- size_t ret = 0;
- data_holder_t *data = (data_holder_t *)datap;
- data_holder_t temp;
- off_t where;
+ dns_name_t *name;
- UNUSED(arg);
+ length = strlen(namestr);
- REQUIRE(file != NULL);
- REQUIRE(crc != NULL);
- REQUIRE(data != NULL);
- REQUIRE((data->len == 0 && data->data == NULL) ||
- (data->len != 0 && data->data != NULL));
-
- result = isc_stdio_tell(file, &where);
- if (result != ISC_R_SUCCESS)
- return (result);
-
- temp = *data;
- temp.data = (data->len == 0
- ? NULL
- : (char *)((uintptr_t)where + sizeof(data_holder_t)));
-
- isc_crc64_update(crc, (void *)&temp, sizeof(temp));
- ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
- if (ret != 1)
- return (ISC_R_FAILURE);
- if (data->len > 0) {
- isc_crc64_update(crc, (const void *)data->data, data->len);
- ret = fwrite(data->data, data->len, 1, file);
- if (ret != 1)
- return (ISC_R_FAILURE);
- }
+ result = isc_buffer_allocate(mctx, &b, length);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_putmem(b, (const unsigned char *) namestr, length);
+
+ dns_fixedname_init(fname);
+ name = dns_fixedname_name(fname);
+ ATF_REQUIRE(name != NULL);
+ result = dns_name_fromtext(name, b, dns_rootname, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- return (ISC_R_SUCCESS);
+ isc_buffer_free(&b);
}
-static isc_result_t
-fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
- isc_uint64_t *crc)
-{
- data_holder_t *data = p->data;
- size_t size;
+static test_context_t *
+test_context_setup(void) {
+ test_context_t *ctx;
+ isc_result_t result;
+ size_t i;
- UNUSED(base);
- UNUSED(max);
- UNUSED(arg);
+ ctx = isc_mem_get(mctx, sizeof(*ctx));
- REQUIRE(crc != NULL);
- REQUIRE(p != NULL);
+ ctx->rbt = NULL;
+ result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ctx->rbt_distances = NULL;
+ result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt_distances);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- if (data == NULL)
- printf("fixing data: data NULL\n");
- else
- printf("fixing data: len %d, data %p\n", data->len, data->data);
+ for (i = 0; i < domain_names_count; i++) {
+ size_t *n;
+ dns_fixedname_t fname;
+ dns_name_t *name;
- if (data == NULL ||
- (data->len == 0 && data->data != NULL) ||
- (data->len != 0 && data->data == NULL))
- return (ISC_R_INVALIDFILE);
+ build_name_from_str(domain_names[i], &fname);
- size = max - ((char *)p - (char *)base);
+ name = dns_fixedname_name(&fname);
- if (data->len > (int) size || data->data > (const char *) max) {
- printf("data invalid\n");
- return (ISC_R_INVALIDFILE);
- }
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = i + 1;
+ result = dns_rbt_addname(ctx->rbt, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- isc_crc64_update(crc, (void *)data, sizeof(*data));
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = node_distances[i];
+ result = dns_rbt_addname(ctx->rbt_distances, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
- data->data = (data->len == 0)
- ? NULL
- : (char *)data + sizeof(data_holder_t);
+ return (ctx);
+}
- if (data->len > 0)
- isc_crc64_update(crc, (const void *)data->data, data->len);
+static void
+test_context_teardown(test_context_t *ctx) {
+ dns_rbt_destroy(&ctx->rbt);
+ dns_rbt_destroy(&ctx->rbt_distances);
- return (ISC_R_SUCCESS);
+ isc_mem_put(mctx, ctx, sizeof(*ctx));
}
/*
- * Load test data into the RBT.
+ * Walk the tree and ensure that all the test nodes are present.
*/
static void
-add_test_data(isc_mem_t *mctx, dns_rbt_t *rbt) {
- char buffer[1024];
- isc_buffer_t b;
+check_test_data(dns_rbt_t *rbt) {
+ dns_fixedname_t fixed;
isc_result_t result;
- dns_fixedname_t fname;
- dns_name_t *name;
- dns_compress_t cctx;
- rbt_testdata_t *testdatap = testdata;
+ dns_name_t *foundname;
+ size_t i;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
- dns_compress_init(&cctx, -1, mctx);
+ for (i = 0; i < domain_names_count; i++) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ size_t *n;
- while (testdatap->name != NULL && testdatap->data.data != NULL) {
- memmove(buffer, testdatap->name, testdatap->name_len);
+ build_name_from_str(domain_names[i], &fname);
- isc_buffer_init(&b, buffer, testdatap->name_len);
- isc_buffer_add(&b, testdatap->name_len);
- dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
- result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
- if (result != ISC_R_SUCCESS) {
- testdatap++;
- continue;
- }
-
- if (name != NULL) {
- result = dns_rbt_addname(rbt, name, &testdatap->data);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
- }
- testdatap++;
+ n = NULL;
+ result = dns_rbt_findname(rbt, name, 0, foundname,
+ (void *) &n);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(*n, i + 1);
}
+}
- dns_compress_invalidate(&cctx);
+ATF_TC(rbt_create);
+ATF_TC_HEAD(rbt_create, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test the creation of an rbt");
}
+ATF_TC_BODY(rbt_create, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ isc_boolean_t tree_ok;
-/*
- * Walk the tree and ensure that all the test nodes are present.
- */
-static void
-check_test_data(dns_rbt_t *rbt) {
- char buffer[1024];
- char *arg;
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ check_test_data(ctx->rbt);
+
+ tree_ok = dns__rbt_checkproperties(ctx->rbt);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_nodecount);
+ATF_TC_HEAD(rbt_nodecount, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test dns_rbt_nodecount() on a tree");
+}
+ATF_TC_BODY(rbt_nodecount, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbtnode_get_distance);
+ATF_TC_HEAD(rbtnode_get_distance, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test dns_rbtnode_get_distance() on a tree");
+}
+ATF_TC_BODY(rbtnode_get_distance, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ const char *name_str = "a";
dns_fixedname_t fname;
- dns_fixedname_t fixed;
dns_name_t *name;
- isc_buffer_t b;
- data_holder_t *data;
- isc_result_t result;
- dns_name_t *foundname;
- rbt_testdata_t *testdatap = testdata;
+ dns_rbtnode_t *node = NULL;
+ dns_rbtnodechain_t chain;
- dns_fixedname_init(&fixed);
- foundname = dns_fixedname_name(&fixed);
+ UNUSED(tc);
- while (testdatap->name != NULL && testdatap->data.data != NULL) {
- memmove(buffer, testdatap->name, testdatap->name_len + 1);
- arg = buffer;
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
- isc_buffer_init(&b, arg, testdatap->name_len);
- isc_buffer_add(&b, testdatap->name_len);
- dns_fixedname_init(&fname);
- name = dns_fixedname_name(&fname);
- result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
- if (result != ISC_R_SUCCESS) {
- testdatap++;
- continue;
- }
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
- data = NULL;
- result = dns_rbt_findname(rbt, name, 0, foundname,
- (void *) &data);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ ctx = test_context_setup();
+
+ build_name_from_str(name_str, &fname);
+ name = dns_fixedname_name(&fname);
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ result = dns_rbt_findnode(ctx->rbt_distances, name, NULL,
+ &node, &chain, 0, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
- testdatap++;
+ while (node != NULL) {
+ const size_t *distance = (const size_t *) node->data;
+ if (distance != NULL)
+ ATF_CHECK_EQ(*distance,
+ dns__rbtnode_getdistance(node));
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result == ISC_R_NOMORE)
+ break;
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
}
+
+ ATF_CHECK_EQ(result, ISC_R_NOMORE);
+
+ dns_rbtnodechain_invalidate(&chain);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
}
-static void
-data_printer(FILE *out, void *datap)
-{
- data_holder_t *data = (data_holder_t *)datap;
+ATF_TC(rbt_check_distance_random);
+ATF_TC_HEAD(rbt_check_distance_random, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test tree balance, inserting names in random order");
+}
+ATF_TC_BODY(rbt_check_distance_random, tc) {
+ /* This test checks an important performance-related property of
+ * the red-black tree, which is important for us: the longest
+ * path from a sub-tree's root to a node is no more than
+ * 2log(n). This check verifies that the tree is balanced.
+ */
+ dns_rbt_t *mytree = NULL;
+ const int log_num_nodes = 16;
- fprintf(out, "%d bytes, %s", data->len, data->data);
+ int i;
+ isc_result_t result;
+ isc_boolean_t tree_ok;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Names are inserted in random order. */
+
+ /* Make a large 65536 node top-level domain tree, i.e., the
+ * following code inserts names such as:
+ *
+ * savoucnsrkrqzpkqypbygwoiliawpbmz.
+ * wkadamcbbpjtundbxcmuayuycposvngx.
+ * wzbpznemtooxdpjecdxynsfztvnuyfao.
+ * yueojmhyffslpvfmgyfwioxegfhepnqq.
+ */
+ for (i = 0; i < (1 << log_num_nodes); i++) {
+ size_t *n;
+ char namebuf[34];
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = i + 1;
+
+ while (1) {
+ int j;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ for (j = 0; j < 32; j++) {
+ isc_uint32_t v;
+ isc_random_get(&v);
+ namebuf[j] = 'a' + (v % 26);
+ }
+ namebuf[32] = '.';
+ namebuf[33] = 0;
+
+ build_name_from_str(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_addname(mytree, name, n);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ }
+
+ /* 1 (root . node) + (1 << log_num_nodes) */
+ ATF_CHECK_EQ(1 + (1 << log_num_nodes), dns_rbt_nodecount(mytree));
+
+ /* The distance from each node to its sub-tree root must be less
+ * than 2 * log(n).
+ */
+ ATF_CHECK((2 * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
}
-ATF_TC(rbt);
-ATF_TC_HEAD(rbt, tc) {
- atf_tc_set_md_var(tc, "descr", "Test the creation of an rbt");
+ATF_TC(rbt_check_distance_ordered);
+ATF_TC_HEAD(rbt_check_distance_ordered, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test tree balance, inserting names in sorted order");
}
-ATF_TC_BODY(rbt, tc) {
- dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_check_distance_ordered, tc) {
+ /* This test checks an important performance-related property of
+ * the red-black tree, which is important for us: the longest
+ * path from a sub-tree's root to a node is no more than
+ * 2log(n). This check verifies that the tree is balanced.
+ */
+ dns_rbt_t *mytree = NULL;
+ const int log_num_nodes = 16;
+
+ int i;
isc_result_t result;
+ isc_boolean_t tree_ok;
UNUSED(tc);
isc_mem_debugging = ISC_MEM_DEBUGRECORD;
result = dns_test_begin(NULL, ISC_TRUE);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
- result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
- add_test_data(mctx, rbt);
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- check_test_data(rbt);
+ /* Names are inserted in sorted order. */
- dns_rbt_printall(rbt, data_printer);
+ /* Make a large 65536 node top-level domain tree, i.e., the
+ * following code inserts names such as:
+ *
+ * name00000000.
+ * name00000001.
+ * name00000002.
+ * name00000003.
+ */
+ for (i = 0; i < (1 << log_num_nodes); i++) {
+ size_t *n;
+ char namebuf[14];
+ dns_fixedname_t fname;
+ dns_name_t *name;
- dns_rbt_destroy(&rbt);
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = i + 1;
+
+ snprintf(namebuf, sizeof(namebuf), "name%08x.", i);
+ build_name_from_str(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_addname(mytree, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* 1 (root . node) + (1 << log_num_nodes) */
+ ATF_CHECK_EQ(1 + (1 << log_num_nodes), dns_rbt_nodecount(mytree));
+
+ /* The distance from each node to its sub-tree root must be less
+ * than 2 * log(n).
+ */
+ ATF_CHECK((2 * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+ dns_rbt_destroy(&mytree);
dns_test_end();
}
-ATF_TC(serialize);
-ATF_TC_HEAD(serialize, tc) {
- atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
+static isc_result_t
+insert_helper(dns_rbt_t *rbt, const char *namestr, dns_rbtnode_t **node) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(namestr, &fname);
+ name = dns_fixedname_name(&fname);
+
+ return (dns_rbt_addnode(rbt, name, node));
+}
+
+static isc_boolean_t
+compare_labelsequences(dns_rbtnode_t *node, const char *labelstr) {
+ dns_name_t name;
+ isc_result_t result;
+ char *nodestr = NULL;
+ isc_boolean_t is_equal;
+
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(node, &name);
+
+ result = dns_name_tostring(&name, &nodestr, mctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ is_equal = strcmp(labelstr, nodestr) == 0 ? ISC_TRUE : ISC_FALSE;
+
+ isc_mem_free(mctx, nodestr);
+
+ return (is_equal);
+}
+
+ATF_TC(rbt_insert);
+ATF_TC_HEAD(rbt_insert, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test insertion into a tree");
}
-ATF_TC_BODY(serialize, tc) {
- dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_insert, tc) {
isc_result_t result;
- FILE *rbtfile = NULL;
- dns_rbt_t *rbt_deserialized = NULL;
- off_t offset;
- int fd;
- off_t filesize = 0;
- char *base;
+ test_context_t *ctx;
+ dns_rbtnode_t *node;
UNUSED(tc);
isc_mem_debugging = ISC_MEM_DEBUGRECORD;
result = dns_test_begin(NULL, ISC_TRUE);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
- result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
- ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
- add_test_data(mctx, rbt);
+ ctx = test_context_setup();
- dns_rbt_printall(rbt, data_printer);
+ /* Check node count before beginning. */
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
- /*
- * Serialize the tree.
+ /* Try to insert a node that already exists. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+ /* Node count must not have changed. */
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+ /* Try to insert a node that doesn't exist. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "0", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "0"), ISC_TRUE);
+
+ /* Node count must have increased. */
+ ATF_CHECK_EQ(16, dns_rbt_nodecount(ctx->rbt));
+
+ /* Another. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "example.com", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE(node != NULL);
+ ATF_CHECK_EQ(node->data, NULL);
+
+ /* Node count must have increased. */
+ ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+ /* Re-adding it should return EXISTS */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "example.com", &node);
+ ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+ /* Node count must not have changed. */
+ ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+ /* Fission the node d.e.f */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "k.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "k"), ISC_TRUE);
+
+ /* Node count must have incremented twice ("d.e.f" fissioned to
+ * "d" and "e.f", and the newly added "k").
+ */
+ ATF_CHECK_EQ(19, dns_rbt_nodecount(ctx->rbt));
+
+ /* Fission the node "g.h" */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "h", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "h"), ISC_TRUE);
+
+ /* Node count must have incremented ("g.h" fissioned to "g" and
+ * "h").
*/
- printf("serialization begins.\n");
- rbtfile = fopen("./zone.bin", "w+b");
- ATF_REQUIRE(rbtfile != NULL);
- result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
- &offset);
- ATF_REQUIRE(result == ISC_R_SUCCESS);
- dns_rbt_destroy(&rbt);
+ ATF_CHECK_EQ(20, dns_rbt_nodecount(ctx->rbt));
+
+ /* Add child domains */
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "m.p.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "m"), ISC_TRUE);
+ ATF_CHECK_EQ(21, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "n.p.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "n"), ISC_TRUE);
+ ATF_CHECK_EQ(22, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "l.a", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "l"), ISC_TRUE);
+ ATF_CHECK_EQ(23, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "r.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ node = NULL;
+ result = insert_helper(ctx->rbt, "s.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(25, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "h.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Add more nodes one by one to cover left and right rotation
+ * functions.
+ */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "m", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "nm", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "om", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "k", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "l", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "fe", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "ge", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "i", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ node = NULL;
+ result = insert_helper(ctx->rbt, "ae", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "n", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_remove);
+ATF_TC_HEAD(rbt_remove, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test removal from a tree");
+}
+ATF_TC_BODY(rbt_remove, tc) {
/*
- * Deserialize the tree
+ * This testcase checks that after node removal, the
+ * binary-search tree is valid and all nodes that are supposed
+ * to exist are present in the correct order. It mainly tests
+ * DomainTree as a BST, and not particularly as a red-black
+ * tree. This test checks node deletion when upper nodes have
+ * data.
*/
- printf("deserialization begins.\n");
+ isc_result_t result;
+ size_t j;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
/*
- * Map in the whole file in one go
+ * Delete single nodes and check if the rest of the nodes exist.
*/
- fd = open("zone.bin", O_RDWR);
- isc_file_getsizefd(fd, &filesize);
- base = mmap(NULL, filesize,
- PROT_READ|PROT_WRITE,
- MAP_FILE|MAP_PRIVATE, fd, 0);
- ATF_REQUIRE(base != NULL && base != MAP_FAILED);
- close(fd);
-
- result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
- delete_data, NULL, fix_data, NULL,
- NULL, &rbt_deserialized);
-
- /* Test to make sure we have a valid tree */
- ATF_REQUIRE(result == ISC_R_SUCCESS);
- if (rbt_deserialized == NULL)
- atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
-
- check_test_data(rbt_deserialized);
-
- dns_rbt_printall(rbt_deserialized, data_printer);
-
- dns_rbt_destroy(&rbt_deserialized);
- munmap(base, filesize);
- unlink("zone.bin");
+ for (j = 0; j < ordered_names_count; j++) {
+ dns_rbt_t *mytree = NULL;
+ dns_rbtnode_t *node;
+ size_t i;
+ size_t *n;
+ isc_boolean_t tree_ok;
+ dns_rbtnodechain_t chain;
+ size_t start_node;
+
+ /* Create a tree. */
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Insert test data into the tree. */
+ for (i = 0; i < domain_names_count; i++) {
+ node = NULL;
+ result = insert_helper(mytree, domain_names[i], &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* Check that all names exist in order. */
+ for (i = 0; i < ordered_names_count; i++) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[i], &fname);
+
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Add node data */
+ ATF_REQUIRE(node != NULL);
+ ATF_REQUIRE_EQ(node->data, NULL);
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = i;
+
+ node->data = n;
+ }
+
+ /* Now, delete the j'th node from the tree. */
+ {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[j], &fname);
+
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_deletename(mytree, name, ISC_FALSE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* Check RB tree properties. */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ /* Now, walk through nodes in order. */
+ if (j == 0) {
+ /*
+ * Node for ordered_names[0] was already deleted
+ * above. We start from node 1.
+ */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
+
+ build_name_from_str(ordered_names[1], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 1;
+ } else {
+ /* Start from node 0. */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 0;
+ }
+
+ /*
+ * node and chain have been set by the code above at
+ * this point.
+ */
+ for (i = start_node; i < ordered_names_count; i++) {
+ dns_fixedname_t fname_j, fname_i;
+ dns_name_t *name_j, *name_i;
+
+ build_name_from_str(ordered_names[j], &fname_j);
+ name_j = dns_fixedname_name(&fname_j);
+ build_name_from_str(ordered_names[i], &fname_i);
+ name_i = dns_fixedname_name(&fname_i);
+
+ if (dns_name_equal(name_i, name_j)) {
+ /*
+ * This may be true for the last node if
+ * we seek ahead in the loop using
+ * dns_rbtnodechain_next() below.
+ */
+ if (node == NULL) {
+ break;
+ }
+
+ /* All ordered nodes have data
+ * initially. If any node is empty, it
+ * means it was removed, but an empty
+ * node exists because it is a
+ * super-domain. Just skip it.
+ */
+ if (node->data == NULL) {
+ result = dns_rbtnodechain_next(&chain,
+ NULL,
+ NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain,
+ NULL,
+ NULL,
+ &node);
+ }
+ }
+ continue;
+ }
+
+ ATF_REQUIRE(node != NULL);
+
+ n = (size_t *) node->data;
+ if (n != NULL) {
+ /* printf("n=%zu, i=%zu\n", *n, i); */
+ ATF_CHECK_EQ(*n, i);
+ }
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ }
+ }
+
+ /* We should have reached the end of the tree. */
+ ATF_REQUIRE_EQ(node, NULL);
+
+ dns_rbt_destroy(&mytree);
+ }
+
dns_test_end();
}
-ATF_TC(deserialize_corrupt);
-ATF_TC_HEAD(deserialize_corrupt, tc) {
- atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
+ATF_TC(rbt_remove_empty);
+ATF_TC_HEAD(rbt_remove_empty, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test removal from a tree when "
+ "upper nodes are empty");
}
-ATF_TC_BODY(deserialize_corrupt, tc) {
- dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_remove_empty, tc) {
+ /*
+ * This test is similar to the rbt_remove test, but checks node
+ * deletion when upper nodes are empty.
+ */
isc_result_t result;
- FILE *rbtfile = NULL;
- off_t offset;
- int fd;
- off_t filesize = 0;
- char *base, *p, *q;
- isc_uint32_t r;
- int i;
+ size_t j;
UNUSED(tc);
result = dns_test_begin(NULL, ISC_TRUE);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- /* Set up map file */
- result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
- ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ /*
+ * Delete single nodes and check if the rest of the nodes exist.
+ */
+ for (j = 0; j < ordered_names_count; j++) {
+ dns_rbt_t *mytree = NULL;
+ dns_rbtnode_t *node;
+ size_t i;
+ isc_boolean_t tree_ok;
+ dns_rbtnodechain_t chain;
+ size_t start_node;
+
+ /* Create a tree. */
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Insert test data into the tree. */
+ for (i = 0; i < domain_names_count; i++) {
+ node = NULL;
+ result = insert_helper(mytree, domain_names[i], &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* Check that all names exist in order. */
+ for (i = 0; i < ordered_names_count; i++) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[i], &fname);
+
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Check that node data is empty */
+ ATF_REQUIRE(node != NULL);
+ ATF_REQUIRE_EQ(node->data, NULL);
+ }
+
+ /* Now, delete the j'th node from the tree. */
+ {
+ dns_fixedname_t fname;
+ dns_name_t *name;
- add_test_data(mctx, rbt);
- rbtfile = fopen("./zone.bin", "w+b");
- ATF_REQUIRE(rbtfile != NULL);
- result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
- &offset);
- ATF_REQUIRE(result == ISC_R_SUCCESS);
- dns_rbt_destroy(&rbt);
-
- /* Read back with random fuzzing */
- for (i = 0; i < 256; i++) {
- dns_rbt_t *rbt_deserialized = NULL;
-
- fd = open("zone.bin", O_RDWR);
- isc_file_getsizefd(fd, &filesize);
- base = mmap(NULL, filesize,
- PROT_READ|PROT_WRITE,
- MAP_FILE|MAP_PRIVATE, fd, 0);
- ATF_REQUIRE(base != NULL && base != MAP_FAILED);
- close(fd);
-
- /* Randomly fuzz a portion of the memory */
- isc_random_get(&r);
- p = base + (r % filesize);
- q = base + filesize;
- isc_random_get(&r);
- q -= (r % (q - p));
- while (p++ < q) {
- isc_random_get(&r);
- *p = r & 0xff;
+ build_name_from_str(ordered_names[j], &fname);
+
+ name = dns_fixedname_name(&fname);
+
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL, &node,
+ NULL, DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_deletenode(mytree, node, ISC_FALSE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
}
- result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
- delete_data, NULL,
- fix_data, NULL,
- NULL, &rbt_deserialized);
- printf("%d: %s\n", i, isc_result_totext(result));
+ /* Check RB tree properties. */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ /* Now, walk through nodes in order. */
+ if (j == 0) {
+ /*
+ * Node for ordered_names[0] was already deleted
+ * above. We start from node 1.
+ */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK(result != ISC_R_SUCCESS);
+
+ build_name_from_str(ordered_names[1], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 1;
+ } else {
+ /* Start from node 0. */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ build_name_from_str(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 0;
+ }
- /* Test to make sure we have a valid tree */
- ATF_REQUIRE(result == ISC_R_SUCCESS ||
- result == ISC_R_INVALIDFILE);
- if (result != ISC_R_SUCCESS)
- ATF_REQUIRE(rbt_deserialized == NULL);
+ /*
+ * node and chain have been set by the code above at
+ * this point.
+ */
+ for (i = start_node; i < ordered_names_count; i++) {
+ dns_fixedname_t fntmp1, fntmp2;
+ dns_name_t *ntmp1, *ntmp2;
+ dns_fixedname_t fname_j, fname_i;
+ dns_name_t *name_j, *name_i;
+
+ build_name_from_str("j.z.d.e.f", &fntmp1);
+ ntmp1 = dns_fixedname_name(&fntmp1);
+ build_name_from_str("z.d.e.f", &fntmp2);
+ ntmp2 = dns_fixedname_name(&fntmp2);
+
+ build_name_from_str(ordered_names[j], &fname_j);
+ name_j = dns_fixedname_name(&fname_j);
+ build_name_from_str(ordered_names[i], &fname_i);
+ name_i = dns_fixedname_name(&fname_i);
+
+ if (dns_name_equal(name_j, ntmp1) &&
+ dns_name_equal(name_j, ntmp2))
+ {
+ /*
+ * The only special case in the
+ * tree. Here, "z.d.e.f" will not exist
+ * as it would have been removed during
+ * removal of "j.z.d.e.f".
+ */
+ continue;
+ }
+
+ if (dns_name_equal(name_i, name_j)) {
+ dns_fixedname_t fntmp3, fntmp4, fntmp5, fntmp6;
+ dns_name_t *ntmp3, *ntmp4, *ntmp5, *ntmp6;
+
+ /*
+ * This may be true for the last node if
+ * we seek ahead in the loop using
+ * dns_rbtnodechain_next() below.
+ */
+ if (node == NULL) {
+ break;
+ }
+
+ /*
+ * All ordered nodes are empty
+ * initially. If an empty removed node
+ * exists because it is a super-domain,
+ * just skip it.
+ */
+ build_name_from_str("d.e.f", &fntmp3);
+ ntmp3 = dns_fixedname_name(&fntmp3);
+ build_name_from_str("w.y.d.e.f", &fntmp4);
+ ntmp4 = dns_fixedname_name(&fntmp4);
+ build_name_from_str("z.d.e.f", &fntmp5);
+ ntmp5 = dns_fixedname_name(&fntmp5);
+ build_name_from_str("g.h", &fntmp6);
+ ntmp6 = dns_fixedname_name(&fntmp6);
+
+ if (dns_name_equal(name_j, ntmp3) ||
+ dns_name_equal(name_j, ntmp4) ||
+ dns_name_equal(name_j, ntmp5) ||
+ dns_name_equal(name_j, ntmp6))
+ {
+ result = dns_rbtnodechain_next(&chain,
+ NULL,
+ NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain,
+ NULL,
+ NULL,
+ &node);
+ }
+ }
+ continue;
+ }
+
+ ATF_REQUIRE(node != NULL);
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ }
+ }
- if (rbt_deserialized != NULL)
- dns_rbt_destroy(&rbt_deserialized);
+ /* We should have reached the end of the tree. */
+ ATF_REQUIRE_EQ(node, NULL);
- munmap(base, filesize);
+ dns_rbt_destroy(&mytree);
}
- unlink("zone.bin");
dns_test_end();
}
+static void
+insert_nodes(dns_rbt_t *mytree, char **names,
+ size_t *names_count, isc_uint32_t num_names)
+{
+ isc_uint32_t i;
+
+ for (i = 0; i < num_names; i++) {
+ size_t *n;
+ char namebuf[34];
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ *n = i; /* Unused value */
+
+ while (1) {
+ int j;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ for (j = 0; j < 32; j++) {
+ isc_uint32_t v;
+ isc_random_get(&v);
+ namebuf[j] = 'a' + (v % 26);
+ }
+ namebuf[32] = '.';
+ namebuf[33] = 0;
+
+ build_name_from_str(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_addname(mytree, name, n);
+ if (result == ISC_R_SUCCESS) {
+ names[*names_count] = isc_mem_strdup(mctx,
+ namebuf);
+ *names_count += 1;
+ break;
+ }
+ }
+ }
+}
-ATF_TC(serialize_align);
-ATF_TC_HEAD(serialize_align, tc) {
+static void
+remove_nodes(dns_rbt_t *mytree, char **names,
+ size_t *names_count, isc_uint32_t num_names)
+{
+ isc_uint32_t i;
+
+ UNUSED(mytree);
+
+ for (i = 0; i < num_names; i++) {
+ isc_uint32_t node;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ isc_random_get(&node);
+
+ node %= *names_count;
+
+ build_name_from_str(names[node], &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_deletename(mytree, name, ISC_FALSE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ isc_mem_free(mctx, names[node]);
+ if (*names_count > 0) {
+ names[node] = names[*names_count - 1];
+ names[*names_count - 1] = NULL;
+ *names_count -= 1;
+ }
+ }
+}
+
+static void
+check_tree(dns_rbt_t *mytree, char **names, size_t names_count) {
+ isc_boolean_t tree_ok;
+
+ UNUSED(names);
+
+ ATF_CHECK_EQ(names_count + 1, dns_rbt_nodecount(mytree));
+
+ /*
+ * The distance from each node to its sub-tree root must be less
+ * than 2 * log_2(1024).
+ */
+ ATF_CHECK((2 * 10) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+}
+
+ATF_TC(rbt_insert_and_remove);
+ATF_TC_HEAD(rbt_insert_and_remove, tc) {
atf_tc_set_md_var(tc, "descr",
- "Test the dns_rbt_serialize_align() function.");
+ "Test insert and remove in a loop");
}
-ATF_TC_BODY(serialize_align, tc) {
+ATF_TC_BODY(rbt_insert_and_remove, tc) {
+ /*
+ * What is the best way to test our red-black tree code? It is
+ * not a good method to test every case handled in the actual
+ * code itself. This is because our approach itself may be
+ * incorrect.
+ *
+ * We test our code at the interface level here by exercising the
+ * tree randomly multiple times, checking that red-black tree
+ * properties are valid, and all the nodes that are supposed to be
+ * in the tree exist and are in order.
+ *
+ * NOTE: These tests are run within a single tree level in the
+ * forest. The number of nodes in the tree level doesn't grow
+ * over 1024.
+ */
+ isc_result_t result;
+ dns_rbt_t *mytree = NULL;
+ /*
+ * We use an array for storing names instead of a set
+ * structure. It's slow, but works and is good enough for tests.
+ */
+ char *names[1024];
+ size_t names_count;
+ int i;
+
UNUSED(tc);
- ATF_CHECK(dns_rbt_serialize_align(0) == 0);
- ATF_CHECK(dns_rbt_serialize_align(1) == 8);
- ATF_CHECK(dns_rbt_serialize_align(2) == 8);
- ATF_CHECK(dns_rbt_serialize_align(3) == 8);
- ATF_CHECK(dns_rbt_serialize_align(4) == 8);
- ATF_CHECK(dns_rbt_serialize_align(5) == 8);
- ATF_CHECK(dns_rbt_serialize_align(6) == 8);
- ATF_CHECK(dns_rbt_serialize_align(7) == 8);
- ATF_CHECK(dns_rbt_serialize_align(8) == 8);
- ATF_CHECK(dns_rbt_serialize_align(9) == 16);
- ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
- ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, ISC_TRUE);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ memset(names, 0, sizeof(names));
+ names_count = 0;
+
+ /* Repeat the insert/remove test some 4096 times */
+ for (i = 0; i < 4096; i++) {
+ isc_uint32_t num_names;
+ isc_random_get(&num_names);
+
+ if (names_count < 1024) {
+ num_names %= 1024 - names_count;
+ num_names++;
+ } else {
+ num_names = 0;
+ }
+
+ insert_nodes(mytree, names, &names_count, num_names);
+ check_tree(mytree, names, names_count);
+
+ isc_random_get(&num_names);
+ if (names_count > 0) {
+ num_names %= names_count;
+ num_names++;
+ } else {
+ num_names = 0;
+ }
+
+ remove_nodes(mytree, names, &names_count, num_names);
+ check_tree(mytree, names, names_count);
+ }
+
+ /* Remove the rest of the nodes */
+ remove_nodes(mytree, names, &names_count, names_count);
+ check_tree(mytree, names, names_count);
+
+ for (i = 0; i < 1024; i++) {
+ if (names[i] != NULL) {
+ isc_mem_free(mctx, names[i]);
+ }
+ }
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
}
/*
* Main
*/
ATF_TP_ADD_TCS(tp) {
- ATF_TP_ADD_TC(tp, rbt);
- ATF_TP_ADD_TC(tp, serialize);
- ATF_TP_ADD_TC(tp, deserialize_corrupt);
- ATF_TP_ADD_TC(tp, serialize_align);
+ ATF_TP_ADD_TC(tp, rbt_create);
+ ATF_TP_ADD_TC(tp, rbt_nodecount);
+ ATF_TP_ADD_TC(tp, rbtnode_get_distance);
+ ATF_TP_ADD_TC(tp, rbt_check_distance_random);
+ ATF_TP_ADD_TC(tp, rbt_check_distance_ordered);
+ ATF_TP_ADD_TC(tp, rbt_insert);
+ ATF_TP_ADD_TC(tp, rbt_remove);
+ ATF_TP_ADD_TC(tp, rbt_remove_empty);
+ ATF_TP_ADD_TC(tp, rbt_insert_and_remove);
return (atf_no_error());
}