2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

396 lines
9.4 KiB
C
Raw Normal View History

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <isc/buffer.h>
#include <isc/magic.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/util.h>
#include <dns/name.h>
#include <dns/qp.h>
#include <dns/types.h>
#include "qp_p.h"
#include <tests/qp.h>
/***********************************************************************
*
* key reverse conversions
*/
uint8_t
qp_test_bittoascii(qp_shift_t bit) {
uint8_t byte = dns_qp_byte_for_bit[bit];
if (bit == SHIFT_NOBYTE) {
return ('.');
} else if (qp_common_character(byte)) {
return (byte);
} else if (byte < '-') {
return ('#');
} else if (byte < '_') {
return ('@');
} else {
return ('~' - SHIFT_OFFSET + bit);
}
}
const char *
qp_test_keytoascii(dns_qpkey_t key, size_t len) {
for (size_t offset = 0; offset < len; offset++) {
key[offset] = qp_test_bittoascii(key[offset]);
}
key[len] = '\0';
return ((const char *)key);
}
void
qp_test_keytoname(const dns_qpkey_t key, dns_name_t *name) {
size_t locs[128];
size_t loc = 0, opos = 0;
size_t offset;
REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
REQUIRE(name->buffer != NULL);
REQUIRE(name->offsets != NULL);
isc_buffer_clear(name->buffer);
/* Scan the key looking for label boundaries */
for (offset = 0; offset < 512; offset++) {
INSIST(key[offset] >= SHIFT_NOBYTE &&
key[offset] < SHIFT_OFFSET);
INSIST(loc < 128);
if (key[offset] == SHIFT_NOBYTE) {
if (key[offset + 1] == SHIFT_NOBYTE) {
locs[loc] = offset + 1;
break;
}
locs[loc++] = offset + 1;
} else if (offset == 0) {
/* This happens for a relative name */
locs[loc++] = offset;
}
}
/*
* In the key the labels are encoded in reverse order, so
* we step backward through the label boundaries, then forward
* through the labels, to create the DNS wire format data.
*/
name->labels = loc;
while (loc-- > 0) {
uint8_t len = 0, *lenp = NULL;
/* Add a length byte to the name data and set an offset */
lenp = isc_buffer_used(name->buffer);
isc_buffer_putuint8(name->buffer, 0);
name->offsets[opos++] = name->length++;
/* Convert from escaped byte ranges to ASCII */
for (offset = locs[loc]; offset < locs[loc + 1] - 1; offset++) {
uint8_t byte = dns_qp_byte_for_bit[key[offset]];
if (qp_common_character(byte)) {
isc_buffer_putuint8(name->buffer, byte);
} else {
byte += key[++offset] - SHIFT_BITMAP;
isc_buffer_putuint8(name->buffer, byte);
}
len++;
}
name->length += len;
*lenp = len;
}
/* Add a root label for absolute names */
if (key[0] == SHIFT_NOBYTE) {
name->attributes.absolute = true;
isc_buffer_putuint8(name->buffer, 0);
name->length++;
name->labels++;
}
name->ndata = isc_buffer_base(name->buffer);
}
/***********************************************************************
*
* trie properties
*/
static size_t
getheight(dns_qp_t *qp, qp_node_t *n) {
if (!is_branch(n)) {
return (0);
}
size_t max_height = 0;
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
size_t height = getheight(qp, &twigs[pos]);
max_height = ISC_MAX(max_height, height);
}
return (max_height + 1);
}
size_t
qp_test_getheight(dns_qp_t *qp) {
return (getheight(qp, &qp->root));
}
static size_t
maxkeylen(dns_qp_t *qp, qp_node_t *n) {
if (!is_branch(n)) {
if (leaf_pval(n) == NULL) {
return (0);
} else {
dns_qpkey_t key;
return (leaf_qpkey(qp, n, key));
}
}
size_t max_len = 0;
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
size_t len = maxkeylen(qp, &twigs[pos]);
max_len = ISC_MAX(max_len, len);
}
return (max_len);
}
size_t
qp_test_maxkeylen(dns_qp_t *qp) {
return (maxkeylen(qp, &qp->root));
}
/***********************************************************************
*
* dump to stdout
*/
static void
dumpread(dns_qpreadable_t qpr, const char *type, const char *tail) {
dns_qpread_t *qp = dns_qpreadable_cast(qpr);
printf("%s %p root %p base %p methods %p%s", type, qp, &qp->root,
qp->base, qp->methods, tail);
}
static void
dumpqp(dns_qp_t *qp, const char *type) {
dumpread(qp, type, " mctx ");
printf("%p\n", qp->mctx);
printf("%s %p usage %p generation %u "
"chunk_max %u bump %u fender %u\n",
type, qp, qp->usage, qp->generation, qp->chunk_max, qp->bump,
qp->fender);
printf("%s %p leaf %u live %u used %u free %u hold %u\n", type, qp,
qp->leaf_count, qp->used_count - qp->free_count, qp->used_count,
qp->free_count, qp->hold_count);
printf("%s %p compact_all=%d shared_arrays=%d"
" transaction_mode=%d write_protect=%d\n",
type, qp, qp->compact_all, qp->shared_arrays,
qp->transaction_mode, qp->write_protect);
}
void
qp_test_dumpread(dns_qpreadable_t qp) {
dumpread(qp, "qpread", "\n");
fflush(stdout);
}
void
qp_test_dumpsnap(dns_qpsnap_t *qp) {
dumpread(qp, "qpsnap", " whence ");
printf("%p\n", qp->whence);
fflush(stdout);
}
void
qp_test_dumpqp(dns_qp_t *qp) {
dumpqp(qp, "qp");
fflush(stdout);
}
void
qp_test_dumpmulti(dns_qpmulti_t *multi) {
dumpqp(&multi->phase[0], "qpmulti->phase[0]");
dumpqp(&multi->phase[1], "qpmulti->phase[1]");
printf("qpmulti %p read %p snapshots %u\n", &multi, multi->read,
multi->snapshots);
fflush(stdout);
}
void
qp_test_dumpchunks(dns_qp_t *qp) {
qp_cell_t used = 0;
qp_cell_t free = 0;
dumpqp(qp, "qp");
for (qp_chunk_t c = 0; c < qp->chunk_max; c++) {
printf("qp %p chunk %u base %p used %u free %u generation %u\n",
qp, c, qp->base[c], qp->usage[c].used, qp->usage[c].free,
qp->usage[c].generation);
used += qp->usage[c].used;
free += qp->usage[c].free;
}
printf("qp %p total used %u free %u\n", qp, used, free);
fflush(stdout);
}
void
qp_test_dumptrie(dns_qpreadable_t qpr) {
dns_qpread_t *qp = dns_qpreadable_cast(qpr);
struct {
qp_ref_t ref;
qp_shift_t max, pos;
} stack[512];
size_t sp = 0;
qp_cell_t leaf_count = 0;
/*
* fake up a sentinel stack entry corresponding to the root
* node; the ref is deliberately out of bounds, and pos == max
* so we will immediately stop scanning it
*/
stack[sp].ref = ~0U;
stack[sp].max = 0;
stack[sp].pos = 0;
qp_node_t *n = &qp->root;
printf("%p ROOT\n", n);
for (;;) {
if (is_branch(n)) {
qp_ref_t ref = branch_twigs_ref(n);
qp_weight_t max = branch_twigs_size(n);
qp_node_t *twigs = ref_ptr(qp, ref);
/* brief list of twigs */
dns_qpkey_t bits;
size_t len = 0;
for (qp_shift_t bit = SHIFT_NOBYTE; bit < SHIFT_OFFSET;
bit++)
{
if (branch_has_twig(n, bit)) {
bits[len++] = bit;
}
}
assert(len == max);
qp_test_keytoascii(bits, len);
printf("%*s%p BRANCH %p %d %zu %s\n", (int)sp * 2, "",
n, twigs, ref, branch_key_offset(n), bits);
++sp;
stack[sp].ref = ref;
stack[sp].max = max;
stack[sp].pos = 0;
} else {
if (leaf_pval(n) != NULL) {
dns_qpkey_t key;
qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
printf("%*s%p LEAF %p %d %s\n", (int)sp * 2, "",
n, leaf_pval(n), leaf_ival(n), key);
leaf_count++;
} else {
assert(n == &qp->root);
assert(leaf_count == 0);
printf("%p EMPTY", n);
}
}
while (stack[sp].pos == stack[sp].max) {
if (sp == 0) {
printf("LEAVES %d\n", leaf_count);
fflush(stdout);
return;
}
--sp;
}
n = ref_ptr(qp, stack[sp].ref) + stack[sp].pos;
stack[sp].pos++;
}
}
static void
dumpdot_name(qp_node_t *n) {
if (is_branch(n)) {
qp_ref_t ref = branch_twigs_ref(n);
printf("c%dn%d", ref_chunk(ref), ref_cell(ref));
} else {
printf("v%p", leaf_pval(n));
}
}
static void
dumpdot_twig(dns_qp_t *qp, qp_node_t *n) {
if (is_branch(n)) {
dumpdot_name(n);
printf(" [shape=record, label=\"{ \\N\\noff %zu | ",
branch_key_offset(n));
char sep = '{';
for (qp_shift_t bit = SHIFT_NOBYTE; bit < SHIFT_OFFSET; bit++) {
if (branch_has_twig(n, bit)) {
printf("%c <t%d> %c ", sep,
branch_twig_pos(n, bit),
qp_test_bittoascii(bit));
sep = '|';
}
}
printf("}}\"];\n");
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
dumpdot_name(n);
printf(":t%d:e -> ", pos);
dumpdot_name(&twigs[pos]);
printf(":w;\n");
}
for (qp_weight_t pos = 0; pos < size; pos++) {
dumpdot_twig(qp, &twigs[pos]);
}
} else {
dns_qpkey_t key;
const char *str;
if (leaf_pval(n) == NULL) {
str = "EMPTY";
} else {
str = qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
}
printf("v%p [shape=oval, label=\"\\N ival %d\\n%s\"];\n",
leaf_pval(n), leaf_ival(n), str);
}
}
void
qp_test_dumpdot(dns_qp_t *qp) {
REQUIRE(VALID_QP(qp));
qp_node_t *n = &qp->root;
printf("strict digraph {\nrankdir = \"LR\"; ranksep = 1.0;\n");
printf("ROOT [shape=point]; ROOT -> ");
dumpdot_name(n);
printf(":w;\n");
dumpdot_twig(qp, n);
printf("}\n");
}
/**********************************************************************/