Merge branch 'each-qp-extensions' into 'main'

extensions to QP trie to support DNS databases

See merge request isc-projects/bind9!8338
This commit is contained in:
Evan Hunt
2023-09-28 07:41:51 +00:00
15 changed files with 1152 additions and 557 deletions
+5
View File
@@ -1,3 +1,8 @@
6260. [func] Added opptions to the QP trie that will be needed
when it is used as a zone or cache database: backward
iteration, and retrieval of DNSSEC predecessor
nodes and node chains. [GL !8338]
6259. [placeholder]
6258. [func] Use explictly created external memory pools for
+1 -1
View File
@@ -56,7 +56,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* verify round-trip conversion of first name */
size_t keylen = dns_qpkey_fromname(key, namein);
qp_test_keytoname(key, keylen, nameout);
dns_qpkey_toname(key, keylen, nameout);
assert(dns_name_equal(namein, nameout));
+1 -1
View File
@@ -168,7 +168,7 @@ dns_fwdtable_find(dns_fwdtable_t *fwdtable, const dns_name_t *name,
REQUIRE(VALID_FWDTABLE(fwdtable));
dns_qpmulti_query(fwdtable->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
result = dns_qp_lookup(&qpr, name, NULL, NULL, NULL, &pval, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
dns_forwarders_t *fwdrs = pval;
*forwardersp = fwdrs;
+143 -28
View File
@@ -86,6 +86,7 @@
#include <isc/attributes.h>
#include <dns/name.h>
#include <dns/types.h>
/*%
@@ -115,12 +116,18 @@ typedef struct dns_qpmulti dns_qpmulti_t;
*/
#define DNS_QPREADER_FIELDS \
uint32_t magic; \
uint32_t root_ref; \
dns_qpref_t root_ref; \
dns_qpbase_t *base; \
void *uctx; \
const struct dns_qpmethods *methods
typedef struct dns_qpbase dns_qpbase_t; /* private */
typedef struct dns_qpbase dns_qpbase_t; /* private, declared in qp_p.h */
/*%
* A unique twig reference; this can be converted to chunk and cell
* values to find a specific location.
*/
typedef uint32_t dns_qpref_t;
typedef struct dns_qpreader {
DNS_QPREADER_FIELDS;
@@ -173,28 +180,76 @@ typedef union dns_qpreadable {
*/
#define DNS_QP_MAXKEY 512
/*
* C is not strict enough with its integer types for the following typedefs
* to improve type safety, but it helps to have annotations saying what
* particular kind of number we are dealing with.
*/
/*%
* The bit number, or position of a bit inside a word. (Valid values 0..63)
* A dns_qpkey_t (below) is an array of these; each element within dns_qpkey
* must satisfy:
*
* SHIFT_NOBYTE <= key[off] && key[off] < SHIFT_OFFSET
*/
typedef uint8_t dns_qpshift_t;
/*%
* The number of bits set in a word (i.e, Hamming weight or popcount).
* This is used to determine the position of a node in the packed sparse
* vector of twigs. Valid values are 0..47 (because our bitmap does not
* fill the entire word).
*/
typedef uint8_t dns_qpweight_t;
/*
* Chunk and cell numbers, used to identify a specific location in
* one of the chunks stored in the QP base pointer array. Each cell
* within a chunk can contain a node.
*/
typedef uint32_t dns_qpchunk_t;
typedef uint32_t dns_qpcell_t;
/*%
* A trie lookup key is a small array, allocated on the stack during trie
* searches. Keys are usually created on demand from DNS names using
* `dns_qpkey_fromname()`, but in principle you can define your own
* functions to convert other types to trie lookup keys.
*/
typedef uint8_t dns_qpkey_t[DNS_QP_MAXKEY];
typedef dns_qpshift_t dns_qpkey_t[DNS_QP_MAXKEY];
/*%
* A trie iterator describes a path through the trie from the root to
* a leaf node, for use with `dns_qpiter_init()` and `dns_qpiter_next()`.
* A QP iterator traverses a trie starting with the root and passing
* though each leaf node in lexicographic order; it is used by
* `dns_qpiter_init()` and `dns_qpiter_next()`. It is also used
* internally by `dns_qp_findname_iterator()` to locate the predecessor
* of a searched-for name.
*/
typedef struct dns_qpiter {
unsigned int magic;
dns_qpreader_t *qp;
uint16_t sp;
struct __attribute__((__packed__)) {
uint32_t ref;
uint8_t more;
} stack[DNS_QP_MAXKEY];
dns_qpnode_t *stack[DNS_QP_MAXKEY];
} dns_qpiter_t;
/*%
* A QP chain holds references to each populated node between the root and
* a given leaf. It is used internally by `dns_qp_lookup()` to return a
* partial match if the specific name requested is not found; optionally it
* can be passed back to the caller so that individual nodes can be
* accessed.
*/
typedef struct dns_qpchain {
unsigned int magic;
dns_qpreader_t *qp;
uint8_t len;
struct {
dns_qpnode_t *node;
size_t offset;
} chain[DNS_NAME_MAXLABELS];
} dns_qpchain_t;
/*%
* These leaf methods allow the qp-trie code to call back to the code
* responsible for the leaf values that are stored in the trie. The
@@ -266,13 +321,6 @@ typedef enum dns_qpgc {
DNS_QPGC_ALL,
} dns_qpgc_t;
/*%
* Options for fancy searches such as `dns_qp_findname_ancestor()`
*/
typedef enum dns_qpfind {
DNS_QPFIND_NOEXACT = 1 << 0,
} dns_qpfind_t;
/***********************************************************************
*
* functions - create, destory, enquire
@@ -431,6 +479,17 @@ dns_qpkey_fromname(dns_qpkey_t key, const dns_name_t *name);
* \li the length of the key
*/
void
dns_qpkey_toname(const dns_qpkey_t key, size_t keylen, dns_name_t *name);
/*%<
* Convert a trie lookup key back into a DNS name.
*
* Requires:
* \li `name` is a pointer to a valid `dns_name_t`
* \li `name->buffer` is not NULL
* \li `name->offsets` is not NULL
*/
isc_result_t
dns_qp_getkey(dns_qpreadable_t qpr, const dns_qpkey_t search_key,
size_t search_keylen, void **pval_r, uint32_t *ival_r);
@@ -468,21 +527,39 @@ dns_qp_getname(dns_qpreadable_t qpr, const dns_name_t *name, void **pval_r,
*/
isc_result_t
dns_qp_findname_ancestor(dns_qpreadable_t qpr, const dns_name_t *name,
dns_qpfind_t options, void **pval_r, uint32_t *ival_r);
dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
dns_name_t *foundname, dns_name_t *predecessor,
dns_qpchain_t *chain, void **pval_r, uint32_t *ival_r);
/*%<
* Find a leaf in a qp-trie that is an ancestor domain of, or equal to, the
* given DNS name.
* Look up a leaf in a qp-trie that is equal to, or an ancestor domain of,
* 'name'.
*
* If the DNS_QPFIND_NOEXACT option is set, find the closest ancestor
* domain that is not equal to the search name.
* If 'foundname' is not NULL, it will be updated to contain the name
* that was found (if any). The return code, ISC_R_SUCCESS or
* DNS_R_PARTIALMATCH, indicates whether the name found is name that
* was requested, or an ancestor. If the result is ISC_R_NOTFOUND,
* 'foundname' will not be updated.
*
* The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
* are not null, unless the return value is ISC_R_NOTFOUND.
* If 'chain' is not NULL, it is updated to contain a QP chain with
* references to the populated nodes in the tree between the root and
* the name that was found. If the return code is DNS_R_PARTIALMATCH
* then the chain terminates at the closest ancestor found; if it is
* ISC_R_SUCCESS then it terminates at the name that was requested.
* If the result is ISC_R_NOTFOUND, 'chain' will not be updated.
*
* If 'predecessor' is not NULL, it will be updated to contain the
* closest predecessor of the searched-for name that exists in the
* trie.
*
* The leaf data for the node that was found will be assigned to
* whichever of `*pval_r` and `*ival_r` are not NULL, unless the
* return value is ISC_R_NOTFOUND.
*
* Requires:
* \li `qpr` is a pointer to a readable qp-trie
* \li `name` is a pointer to a valid `dns_name_t`
* \li `foundname` is a pointer to a valid `dns_name_t` with
* buffer and offset space available, or is NULL.
*
* Returns:
* \li ISC_R_SUCCESS if an exact match was found
@@ -557,12 +634,17 @@ dns_qpiter_init(dns_qpreadable_t qpr, dns_qpiter_t *qpi);
*/
isc_result_t
dns_qpiter_next(dns_qpiter_t *qpi, void **pval_r, uint32_t *ival_r);
dns_qpiter_next(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
uint32_t *ival_r);
isc_result_t
dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
uint32_t *ival_r);
/*%<
* Get the next leaf object of a trie in lexicographic order of its keys.
* Iterate forward/backward through a QP trie in lexicographic order.
*
* The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
* are not null, unless the return value is ISC_R_NOMORE.
* are not null, unless the return value is ISC_R_NOMORE. Similarly,
* if `name` is not null, it is updated to contain the node name.
*
* NOTE: see the safety note under `dns_qpiter_init()`.
*
@@ -572,7 +654,7 @@ dns_qpiter_next(dns_qpiter_t *qpi, void **pval_r, uint32_t *ival_r);
* void *pval;
* uint32_t ival;
* dns_qpiter_init(qp, &qpi);
* while (dns_qpiter_next(&qpi, &pval, &ival)) {
* while (dns_qpiter_next(&qpi, &pval, &ival) == ISC_R_SUCCESS) {
* // do something with pval and ival
* }
*
@@ -584,6 +666,39 @@ dns_qpiter_next(dns_qpiter_t *qpi, void **pval_r, uint32_t *ival_r);
* \li ISC_R_NOMORE otherwise
*/
void
dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain);
/*%<
* Initialize a QP chain.
*
* Requires:
* \li `qpr` is a pointer to a valid qp-trie
* \li `chain` is not NULL.
*/
unsigned int
dns_qpchain_length(dns_qpchain_t *chain);
/*%<
* Returns the length of a QP chain.
*
* Requires:
* \li `chain` is a pointer to an initialized QP chain object.
*/
void
dns_qpchain_node(dns_qpchain_t *chain, unsigned int level, dns_name_t *name,
void **pval_r, uint32_t *ival_r);
/*%<
* Sets 'name' to the name of the leaf referenced at `chain->stack[level]`.
*
* The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
* are not null.
*
* Requires:
* \li `chain` is a pointer to an initialized QP chain object.
* \li `level` is less than `chain->len`.
*/
/***********************************************************************
*
* functions - transactions
+1
View File
@@ -144,6 +144,7 @@ typedef struct dns_request dns_request_t;
typedef struct dns_requestmgr dns_requestmgr_t;
typedef struct dns_resolver dns_resolver_t;
typedef struct dns_rpsdb dns_rpsdb_t;
typedef struct dns_qpnode dns_qpnode_t;
typedef uint8_t dns_secalg_t;
typedef uint8_t dns_secproto_t;
typedef struct dns_signature dns_signature_t;
+5 -5
View File
@@ -155,7 +155,7 @@ destroy_keytable(dns_keytable_t *keytable) {
dns_qpmulti_query(keytable->table, &qpr);
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns_keynode_t *n = pval;
dns_keynode_detach(&n);
}
@@ -518,7 +518,7 @@ dns_keytable_finddeepestmatch(dns_keytable_t *keytable, const dns_name_t *name,
REQUIRE(foundname != NULL);
dns_qpmulti_query(keytable->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
result = dns_qp_lookup(&qpr, name, NULL, NULL, NULL, &pval, NULL);
keynode = pval;
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
@@ -547,7 +547,7 @@ dns_keytable_issecuredomain(dns_keytable_t *keytable, const dns_name_t *name,
REQUIRE(wantdnssecp != NULL);
dns_qpmulti_query(keytable->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
result = dns_qp_lookup(&qpr, name, NULL, NULL, NULL, &pval, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
keynode = pval;
if (foundname != NULL) {
@@ -664,7 +664,7 @@ dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) {
dns_qpmulti_query(keytable->table, &qpr);
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns_keynode_t *knode = pval;
if (knode->dslist != NULL) {
result = keynode_dslist_totext(knode, text);
@@ -692,7 +692,7 @@ dns_keytable_forall(dns_keytable_t *keytable,
dns_qpmulti_query(keytable->table, &qpr);
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns_keynode_t *knode = pval;
(*func)(keytable, knode, knode->name, arg);
}
+2 -5
View File
@@ -289,12 +289,9 @@ dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
REQUIRE(VALID_NAMETREE(nametree));
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, (void **)&node, NULL);
result = dns_qp_lookup(&qpr, name, found, NULL, NULL, (void **)&node,
NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
if (found != NULL) {
dns_name_copy(node->name, found);
}
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
ret = node->set;
+4 -4
View File
@@ -413,7 +413,7 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
dns_qpmulti_query(ntatable->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
result = dns_qp_lookup(&qpr, name, NULL, NULL, NULL, &pval, NULL);
nta = pval;
switch (result) {
@@ -479,7 +479,7 @@ dns_ntatable_totext(dns_ntatable_t *ntatable, const char *view,
dns_qpmulti_query(ntatable->table, &qpr);
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns__nta_t *n = pval;
char nbuf[DNS_NAME_FORMATSIZE];
char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
@@ -535,7 +535,7 @@ dns_ntatable_save(dns_ntatable_t *ntatable, FILE *fp) {
dns_qpmulti_query(ntatable->table, &qpr);
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns__nta_t *n = pval;
isc_buffer_t b;
char nbuf[DNS_NAME_FORMATSIZE + 1], tbuf[80];
@@ -619,7 +619,7 @@ dns_ntatable_shutdown(dns_ntatable_t *ntatable) {
ntatable->shuttingdown = true;
dns_qpiter_init(&qpr, &iter);
while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&iter, NULL, &pval, NULL) == ISC_R_SUCCESS) {
dns__nta_t *n = pval;
dns__nta_shutdown(n);
dns__nta_detach(&n);
+473 -182
View File
File diff suppressed because it is too large Load Diff
+113 -152
View File
@@ -25,37 +25,47 @@
*/
/*
* A qp-trie node is normally either a branch or a leaf. It consists of
* three 32-bit words into which the components are packed. They are used
* as a 64-bit word and a 32-bit word, but they are not declared like that
* to avoid unwanted padding, keeping the size down to 12 bytes. They are
* in native endian order so getting the 64-bit part should compile down to
* an unaligned load.
* A qp-trie node is almost always one of two types: branch or leaf.
* (A third type is used only to anchor the root of a trie; see below.)
*
* The type of node is identified by the tag in the least significant bits
* of the 64-bit word.
* A node contains a 64-bit word and a 32-bit word. In order to avoid
* unwanted padding, they are declared as three 32-bit words; this keeps
* the size down to 12 bytes. They are in native endian order, so getting
* the 64-bit part should compile down to an unaligned load.
*
* In a branch the 64-bit word is described by the enum below. The 32-bit
* word is a reference to the packed sparse vector of "twigs", i.e. child
* nodes. A branch node has at least 2 and less than SHIFT_OFFSET twigs
* (see the enum below). The qp-trie update functions ensure that branches
* actually branch, i.e. branches cannot have only 1 child.
* The node type is identified by the least significant bits of the 64-bit
* word.
*
* The contents of each leaf are set by the trie's user. The 64-bit word
* contains a pointer value (which must be word-aligned, so the tag bits
* are zero), and the 32-bit word is an arbitrary integer value.
* In a leaf node:
* - The 64-bit word is used to store a pointer value. (Pointers must be
* word-aligned so the least significant bits are zero; those bits can
* then act as a node tag to indicate that this is a leaf. This
* requirement is enforced by the make_leaf() constructor.)
* - The 32-bit word is used to store an integer value. Both the
* pointer and integer values can be retrieved when looking up a key.
*
* There is a third kind of node, reader nodes, which anchor the root of a
* trie. A pair of reader nodes together contain a packed `dns_qpreader_t`.
* See the section on "packed reader nodes" below.
* In a branch node:
* - The 64-bit word is subdivided into three portions: the least
* significant bits are the node type (for a branch, 0x1); the
* most sigificant 15 bits are an offset value into the key, and
* the 47 bits in the middle are a bitmap; see the documentation
* for the SHIFT_* enum below.
* - The 32-bit word is a reference (dns_qpref_t) to the packed sparse
* vector of "twigs", i.e. child nodes. A branch node has at least
* two and at most 47 twigs. (The qp-trie update functions ensure that
* branches actually branch, i.e. a branch cannot have only one child.)
*
* A third node type, reader nodes, anchors the root of a trie.
* A pair of reader nodes together contain a packed `dns_qpreader_t`.
* See the section on "packed reader nodes" for details.
*/
typedef struct qp_node {
struct dns_qpnode {
#if WORDS_BIGENDIAN
uint32_t bighi, biglo, small;
#else
uint32_t biglo, bighi, small;
#endif
} qp_node_t;
};
/*
* The possible values of the node type tag. Type tags must fit in two bits
@@ -78,17 +88,13 @@ STATIC_ASSERT(sizeof(void *) <= sizeof(uint64_t),
"pointers must fit in 64 bits");
/*
* A branch node contains a 64-bit word comprising the type tag, the
* The 64-bit word in a branch node is comprised of a node type tag, a
* bitmap, and an offset into the key. It is called an "index word" because
* it describes how to access the twigs vector (think "database index").
* The following enum sets up the bit positions of these parts.
*
* In a leaf, the same 64-bit word contains a pointer. The pointer
* must be word-aligned so that the branch/leaf tag bit is zero.
* This requirement is checked by the newleaf() constructor.
*
* The bitmap is just above the type tag. The `bits_for_byte[]` table is
* used to fill in a key so that bit tests can work directly against the
* The bitmap is just above the type tag. The `dns_qp_bits_for_byte[]` table
* is used to fill in a key so that bit tests can work directly against the
* index word without superfluous masking or shifting; we don't need to
* mask out the bitmap before testing a bit, but we do need to mask the
* bitmap before calling popcount.
@@ -96,7 +102,7 @@ STATIC_ASSERT(sizeof(void *) <= sizeof(uint64_t),
* The byte offset into the key is at the top of the word, so that it
* can be extracted with just a shift, with no masking needed.
*
* The names are SHIFT_thing because they are qp_shift_t values. (See
* The names are SHIFT_thing because they are dns_qpshift_t values. (See
* below for the various `qp_*` type declarations.)
*
* These values are relatively fixed in practice: SHIFT_NOBYTE needs
@@ -116,7 +122,7 @@ enum {
*/
/*
* A "cell" is a location that can contain a `qp_node_t`, and a "chunk"
* A "cell" is a location that can contain a `dns_qpnode_t`, and a "chunk"
* is a moderately large array of cells. A big trie can occupy
* multiple chunks. (Unlike other nodes, a trie's root node lives in
* its `struct dns_qp` instead of being allocated in a cell.)
@@ -142,7 +148,7 @@ STATIC_ASSERT(6 <= QP_CHUNK_LOG && QP_CHUNK_LOG <= 20,
"qp-trie chunk size is unreasonable");
#define QP_CHUNK_SIZE (1U << QP_CHUNK_LOG)
#define QP_CHUNK_BYTES (QP_CHUNK_SIZE * sizeof(qp_node_t))
#define QP_CHUNK_BYTES (QP_CHUNK_SIZE * sizeof(dns_qpnode_t))
/*
* We need a bitfield this big to count how much of a chunk is in use:
@@ -183,71 +189,23 @@ STATIC_ASSERT(6 <= QP_CHUNK_LOG && QP_CHUNK_LOG <= 20,
*/
#define GROWTH_FACTOR(size) ((size) + (size) / 2 + 2)
/***********************************************************************
*
* helper types
*/
/*
* C is not strict enough with its integer types for these typedefs to
* improve type safety, but it helps to have annotations saying what
* particular kind of number we are dealing with.
* Constructors and accessors for dns_qpref_t values, defined here to show
* how the dns_qpref_t, dns_qpchunk_t, dns_qpcell_t types relate to each other
*/
/*
* The number or position of a bit inside a word. (0..63)
*
* Note: A dns_qpkey_t is logically an array of qp_shift_t values, but it
* isn't declared that way because dns_qpkey_t is a public type whereas
* qp_shift_t is private.
*
* A dns_qpkey element key[off] must satisfy
*
* SHIFT_NOBYTE <= key[off] && key[off] < SHIFT_OFFSET
*/
typedef uint8_t qp_shift_t;
/*
* The number of bits set in a word (as in Hamming weight or popcount)
* which is used for the position of a node in the packed sparse
* vector of twigs. (0..47) because our bitmap does not fill the word.
*/
typedef uint8_t qp_weight_t;
/*
* A chunk number, i.e. an index into the chunk arrays.
*/
typedef uint32_t qp_chunk_t;
/*
* Cell offset within a chunk, or a count of cells. Each cell in a
* chunk can contain a node.
*/
typedef uint32_t qp_cell_t;
/*
* A twig reference is used to refer to a twigs vector, which occupies a
* contiguous group of cells.
*/
typedef uint32_t qp_ref_t;
/*
* Constructors and accessors for qp_ref_t values, defined here to show
* how the qp_ref_t, qp_chunk_t, qp_cell_t types relate to each other
*/
static inline qp_ref_t
make_ref(qp_chunk_t chunk, qp_cell_t cell) {
static inline dns_qpref_t
make_ref(dns_qpchunk_t chunk, dns_qpcell_t cell) {
return (QP_CHUNK_SIZE * chunk + cell);
}
static inline qp_chunk_t
ref_chunk(qp_ref_t ref) {
static inline dns_qpchunk_t
ref_chunk(dns_qpref_t ref) {
return (ref / QP_CHUNK_SIZE);
}
static inline qp_cell_t
ref_cell(qp_ref_t ref) {
static inline dns_qpcell_t
ref_cell(dns_qpref_t ref) {
return (ref % QP_CHUNK_SIZE);
}
@@ -256,7 +214,7 @@ ref_cell(qp_ref_t ref) {
* to a value that should trigger an obvious bug. See qp_init()
* and get_root() below.
*/
#define INVALID_REF ((qp_ref_t)~0UL)
#define INVALID_REF ((dns_qpref_t)~0UL)
/***********************************************************************
*
@@ -307,9 +265,9 @@ ref_cell(qp_ref_t ref) {
*/
typedef struct qp_usage {
/*% the allocation point, increases monotonically */
qp_cell_t used : QP_USAGE_BITS;
dns_qpcell_t used : QP_USAGE_BITS;
/*% count of nodes no longer needed, also monotonic */
qp_cell_t free : QP_USAGE_BITS;
dns_qpcell_t free : QP_USAGE_BITS;
/*% qp->base->ptr[chunk] != NULL */
bool exists : 1;
/*% is this chunk shared? [MT] */
@@ -349,7 +307,7 @@ typedef struct qp_usage {
struct dns_qpbase {
unsigned int magic;
isc_refcount_t refcount;
qp_node_t *ptr[];
dns_qpnode_t *ptr[];
};
/*
@@ -362,8 +320,8 @@ typedef struct qp_rcuctx {
struct rcu_head rcu_head;
isc_mem_t *mctx;
dns_qpmulti_t *multi;
qp_chunk_t count;
qp_chunk_t chunk[];
dns_qpchunk_t count;
dns_qpchunk_t chunk[];
} qp_rcuctx_t;
/*
@@ -380,8 +338,8 @@ qpbase_unref(dns_qpreadable_t qpr) {
* Now we know about `dns_qpreader_t` and `dns_qpbase_t`,
* here's how we convert a twig reference into a pointer.
*/
static inline qp_node_t *
ref_ptr(dns_qpreadable_t qpr, qp_ref_t ref) {
static inline dns_qpnode_t *
ref_ptr(dns_qpreadable_t qpr, dns_qpref_t ref) {
dns_qpreader_t *qp = dns_qpreader(qpr);
return (qp->base->ptr[ref_chunk(ref)] + ref_cell(ref));
}
@@ -393,6 +351,7 @@ ref_ptr(dns_qpreadable_t qpr, qp_ref_t ref) {
#define QP_MAGIC ISC_MAGIC('t', 'r', 'i', 'e')
#define QPITER_MAGIC ISC_MAGIC('q', 'p', 'i', 't')
#define QPCHAIN_MAGIC ISC_MAGIC('q', 'p', 'c', 'h')
#define QPMULTI_MAGIC ISC_MAGIC('q', 'p', 'm', 'v')
#define QPREADER_MAGIC ISC_MAGIC('q', 'p', 'r', 'x')
#define QPBASE_MAGIC ISC_MAGIC('q', 'p', 'b', 'p')
@@ -400,6 +359,7 @@ ref_ptr(dns_qpreadable_t qpr, qp_ref_t ref) {
#define QP_VALID(qp) ISC_MAGIC_VALID(qp, QP_MAGIC)
#define QPITER_VALID(qp) ISC_MAGIC_VALID(qp, QPITER_MAGIC)
#define QPCHAIN_VALID(qp) ISC_MAGIC_VALID(qp, QPCHAIN_MAGIC)
#define QPMULTI_VALID(qp) ISC_MAGIC_VALID(qp, QPMULTI_MAGIC)
#define QPBASE_VALID(qp) ISC_MAGIC_VALID(qp, QPBASE_MAGIC)
#define QPRCU_VALID(qp) ISC_MAGIC_VALID(qp, QPRCU_MAGIC)
@@ -407,7 +367,7 @@ ref_ptr(dns_qpreadable_t qpr, qp_ref_t ref) {
/*
* Polymorphic initialization of the `dns_qpreader_t` prefix.
*
* The location of the root node is actually a qp_ref_t, but is
* The location of the root node is actually a dns_qpref_t, but is
* declared in DNS_QPREADER_FIELDS as uint32_t to avoid leaking too
* many internal details into the public API.
*
@@ -506,17 +466,17 @@ struct dns_qp {
/*% array of per-chunk allocation counters */
qp_usage_t *usage;
/*% number of slots in `chunk` and `usage` arrays */
qp_chunk_t chunk_max;
dns_qpchunk_t chunk_max;
/*% which chunk is used for allocations */
qp_chunk_t bump;
dns_qpchunk_t bump;
/*% nodes in the `bump` chunk below `fender` are read only [MT] */
qp_cell_t fender;
dns_qpcell_t fender;
/*% number of leaf nodes */
qp_cell_t leaf_count;
dns_qpcell_t leaf_count;
/*% total of all usage[] counters */
qp_cell_t used_count, free_count;
dns_qpcell_t used_count, free_count;
/*% free cells that cannot be recovered right now */
qp_cell_t hold_count;
dns_qpcell_t hold_count;
/*% what kind of transaction was most recently started [MT] */
enum { QP_NONE, QP_WRITE, QP_UPDATE } transaction_mode : 2;
/*% compact the entire trie [MT] */
@@ -539,7 +499,7 @@ struct dns_qp {
* There is a `reader_ref` which corresponds to the `reader` pointer
* (`ref_ptr(multi->reader_ref) == multi->reader`). The `reader_ref` is
* necessary when freeing the space used by the reader, because there
* isn't a good way to recover a qp_ref_t from a qp_node_t pointer.
* isn't a good way to recover a dns_qpref_t from a dns_qpnode_t pointer.
*
* There is a per-trie list of snapshots that is used for reclaiming
* memory when a snapshot is destroyed.
@@ -550,11 +510,11 @@ struct dns_qp {
struct dns_qpmulti {
uint32_t magic;
/*% RCU-protected pointer to current packed reader */
qp_node_t *reader;
dns_qpnode_t *reader;
/*% the mutex protects the rest of this structure */
isc_mutex_t mutex;
/*% ref_ptr(writer, reader_ref) == reader */
qp_ref_t reader_ref;
dns_qpref_t reader_ref;
/*% the main working structure */
dns_qp_t writer;
/*% saved allocator state to support rollback */
@@ -581,7 +541,7 @@ struct dns_qpmulti {
* Get the 64-bit word of a node.
*/
static inline uint64_t
node64(qp_node_t *n) {
node64(dns_qpnode_t *n) {
uint64_t lo = n->biglo;
uint64_t hi = n->bighi;
return (lo | (hi << 32));
@@ -591,16 +551,16 @@ node64(qp_node_t *n) {
* Get the 32-bit word of a node.
*/
static inline uint32_t
node32(qp_node_t *n) {
node32(dns_qpnode_t *n) {
return (n->small);
}
/*
* Create a node from its parts
*/
static inline qp_node_t
static inline dns_qpnode_t
make_node(uint64_t big, uint32_t small) {
return ((qp_node_t){
return ((dns_qpnode_t){
.biglo = (uint32_t)(big),
.bighi = (uint32_t)(big >> 32),
.small = small,
@@ -612,7 +572,7 @@ make_node(uint64_t big, uint32_t small) {
* a warning about mismatched pointer/integer sizes on 32 bit systems.
*/
static inline void *
node_pointer(qp_node_t *n) {
node_pointer(dns_qpnode_t *n) {
return ((void *)(uintptr_t)(node64(n) & ~TAG_MASK));
}
@@ -620,7 +580,7 @@ node_pointer(qp_node_t *n) {
* Examine a node's tag bits
*/
static inline uint32_t
node_tag(qp_node_t *n) {
node_tag(dns_qpnode_t *n) {
return (n->biglo & TAG_MASK);
}
@@ -628,7 +588,7 @@ node_tag(qp_node_t *n) {
* simplified for the hot path
*/
static inline bool
is_branch(qp_node_t *n) {
is_branch(dns_qpnode_t *n) {
return (n->biglo & BRANCH_TAG);
}
@@ -638,7 +598,7 @@ is_branch(qp_node_t *n) {
* Get a leaf's pointer value.
*/
static inline void *
leaf_pval(qp_node_t *n) {
leaf_pval(dns_qpnode_t *n) {
return (node_pointer(n));
}
@@ -646,16 +606,16 @@ leaf_pval(qp_node_t *n) {
* Get a leaf's integer value
*/
static inline uint32_t
leaf_ival(qp_node_t *n) {
leaf_ival(dns_qpnode_t *n) {
return (node32(n));
}
/*
* Create a leaf node from its parts
*/
static inline qp_node_t
static inline dns_qpnode_t
make_leaf(const void *pval, uint32_t ival) {
qp_node_t leaf = make_node((uintptr_t)pval, ival);
dns_qpnode_t leaf = make_node((uintptr_t)pval, ival);
REQUIRE(node_tag(&leaf) == LEAF_TAG);
return (leaf);
}
@@ -672,15 +632,15 @@ make_leaf(const void *pval, uint32_t ival) {
* Get a branch node's index word
*/
static inline uint64_t
branch_index(qp_node_t *n) {
branch_index(dns_qpnode_t *n) {
return (node64(n));
}
/*
* Get a reference to a branch node's child twigs.
*/
static inline qp_ref_t
branch_twigs_ref(qp_node_t *n) {
static inline dns_qpref_t
branch_twigs_ref(dns_qpnode_t *n) {
return (node32(n));
}
@@ -688,7 +648,7 @@ branch_twigs_ref(qp_node_t *n) {
* Bit positions in the bitmap come directly from the key. DNS names are
* converted to keys using the tables declared at the end of this file.
*/
static inline qp_shift_t
static inline dns_qpshift_t
qpkey_bit(const dns_qpkey_t key, size_t len, size_t offset) {
if (offset < len) {
return (key[offset]);
@@ -701,23 +661,24 @@ qpkey_bit(const dns_qpkey_t key, size_t len, size_t offset) {
* Extract a branch node's offset field, used to index the key.
*/
static inline size_t
branch_key_offset(qp_node_t *n) {
branch_key_offset(dns_qpnode_t *n) {
return ((size_t)(branch_index(n) >> SHIFT_OFFSET));
}
/*
* Which bit identifies the twig of this node for this key?
*/
static inline qp_shift_t
branch_keybit(qp_node_t *n, const dns_qpkey_t key, size_t len) {
static inline dns_qpshift_t
branch_keybit(dns_qpnode_t *n, const dns_qpkey_t key, size_t len) {
return (qpkey_bit(key, len, branch_key_offset(n)));
}
/*
* Get a pointer to a branch node's twigs vector.
* Get a pointer to a the first twig of a branch (this also functions
* as a pointer to the entire twig vector).
*/
static inline qp_node_t *
branch_twigs_vector(dns_qpreadable_t qpr, qp_node_t *n) {
static inline dns_qpnode_t *
branch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
return (ref_ptr(qpr, branch_twigs_ref(n)));
}
@@ -725,8 +686,8 @@ branch_twigs_vector(dns_qpreadable_t qpr, qp_node_t *n) {
* Warm up the cache while calculating which twig we want.
*/
static inline void
prefetch_twigs(dns_qpreadable_t qpr, qp_node_t *n) {
__builtin_prefetch(branch_twigs_vector(qpr, n));
prefetch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
__builtin_prefetch(ref_ptr(qpr, branch_twigs_ref(n)));
}
/* root node **********************************************************/
@@ -734,7 +695,7 @@ prefetch_twigs(dns_qpreadable_t qpr, qp_node_t *n) {
/*
* Get a pointer to the root node, checking if the trie is empty.
*/
static inline qp_node_t *
static inline dns_qpnode_t *
get_root(dns_qpreadable_t qpr) {
dns_qpreader_t *qp = dns_qpreader(qpr);
if (qp->root_ref == INVALID_REF) {
@@ -751,7 +712,7 @@ get_root(dns_qpreadable_t qpr) {
* just enough to treat the root node as a vector of one twig.
*/
#define MOVABLE_ROOT(qp) \
(&(qp_node_t){ \
(&(dns_qpnode_t){ \
.biglo = BRANCH_TAG | (1 << SHIFT_NOBYTE), \
.small = qp->root_ref, \
})
@@ -769,11 +730,11 @@ get_root(dns_qpreadable_t qpr) {
* we subtract 1 to set all lesser bits, and subtract the tag mask
* because the type tag is not part of the bitmap.
*/
static inline qp_weight_t
branch_count_bitmap_before(qp_node_t *n, qp_shift_t bit) {
static inline dns_qpweight_t
branch_count_bitmap_before(dns_qpnode_t *n, dns_qpshift_t bit) {
uint64_t mask = (1ULL << bit) - 1 - TAG_MASK;
uint64_t bitmap = branch_index(n) & mask;
return ((qp_weight_t)__builtin_popcountll(bitmap));
return ((dns_qpweight_t)__builtin_popcountll(bitmap));
}
/*
@@ -782,45 +743,45 @@ branch_count_bitmap_before(qp_node_t *n, qp_shift_t bit) {
* The offset is directly after the bitmap so the offset's lesser bits
* covers the whole bitmap, and the bitmap's weight is the number of twigs.
*/
static inline qp_weight_t
branch_twigs_size(qp_node_t *n) {
static inline dns_qpweight_t
branch_twigs_size(dns_qpnode_t *n) {
return (branch_count_bitmap_before(n, SHIFT_OFFSET));
}
/*
* Position of a twig within the packed sparse vector.
*/
static inline qp_weight_t
branch_twig_pos(qp_node_t *n, qp_shift_t bit) {
static inline dns_qpweight_t
branch_twig_pos(dns_qpnode_t *n, dns_qpshift_t bit) {
return (branch_count_bitmap_before(n, bit));
}
/*
* Get a pointer to a particular twig.
* Get a pointer to the twig for a given bit number.
*/
static inline qp_node_t *
branch_twig_ptr(dns_qpreadable_t qpr, qp_node_t *n, qp_shift_t bit) {
return (branch_twigs_vector(qpr, n) + branch_twig_pos(n, bit));
static inline dns_qpnode_t *
branch_twig_ptr(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpshift_t bit) {
return (ref_ptr(qpr, branch_twigs_ref(n) + branch_twig_pos(n, bit)));
}
/*
* Is the twig identified by this bit present?
*/
static inline bool
branch_has_twig(qp_node_t *n, qp_shift_t bit) {
branch_has_twig(dns_qpnode_t *n, dns_qpshift_t bit) {
return (branch_index(n) & (1ULL << bit));
}
/* twig logistics *****************************************************/
static inline void
move_twigs(qp_node_t *to, qp_node_t *from, qp_weight_t size) {
memmove(to, from, size * sizeof(qp_node_t));
move_twigs(dns_qpnode_t *to, dns_qpnode_t *from, dns_qpweight_t size) {
memmove(to, from, size * sizeof(dns_qpnode_t));
}
static inline void
zero_twigs(qp_node_t *twigs, qp_weight_t size) {
memset(twigs, 0, size * sizeof(qp_node_t));
zero_twigs(dns_qpnode_t *twigs, dns_qpweight_t size) {
memset(twigs, 0, size * sizeof(dns_qpnode_t));
}
/***********************************************************************
@@ -864,14 +825,14 @@ zero_twigs(qp_node_t *twigs, qp_weight_t size) {
* allocated using `alloc_twigs(&multi->writer, READER_SIZE)`.
*/
static inline void
make_reader(qp_node_t *reader, dns_qpmulti_t *multi) {
make_reader(dns_qpnode_t *reader, dns_qpmulti_t *multi) {
dns_qp_t *qp = &multi->writer;
reader[0] = make_node(READER_TAG | (uintptr_t)multi, QPREADER_MAGIC);
reader[1] = make_node(READER_TAG | (uintptr_t)qp->base, qp->root_ref);
}
static inline bool
reader_valid(qp_node_t *reader) {
reader_valid(dns_qpnode_t *reader) {
return (reader != NULL && //
node_tag(&reader[0]) == READER_TAG &&
node_tag(&reader[1]) == READER_TAG &&
@@ -883,7 +844,7 @@ reader_valid(qp_node_t *reader) {
* consistency checks.
*/
static inline dns_qpmulti_t *
unpack_reader(dns_qpreader_t *qp, qp_node_t *reader) {
unpack_reader(dns_qpreader_t *qp, dns_qpnode_t *reader) {
INSIST(reader_valid(reader));
dns_qpmulti_t *multi = node_pointer(&reader[0]);
dns_qpbase_t *base = node_pointer(&reader[1]);
@@ -905,19 +866,19 @@ unpack_reader(dns_qpreader_t *qp, qp_node_t *reader) {
*/
static inline void
attach_leaf(dns_qpreadable_t qpr, qp_node_t *n) {
attach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
dns_qpreader_t *qp = dns_qpreader(qpr);
qp->methods->attach(qp->uctx, leaf_pval(n), leaf_ival(n));
}
static inline void
detach_leaf(dns_qpreadable_t qpr, qp_node_t *n) {
detach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
dns_qpreader_t *qp = dns_qpreader(qpr);
qp->methods->detach(qp->uctx, leaf_pval(n), leaf_ival(n));
}
static inline size_t
leaf_qpkey(dns_qpreadable_t qpr, qp_node_t *n, dns_qpkey_t key) {
leaf_qpkey(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpkey_t key) {
dns_qpreader_t *qp = dns_qpreader(qpr);
size_t len = qp->methods->makekey(key, qp->uctx, leaf_pval(n),
leaf_ival(n));
+14 -8
View File
@@ -171,19 +171,25 @@ dns_zt_find(dns_zt_t *zt, const dns_name_t *name, dns_ztfind_t options,
void *pval = NULL;
dns_ztfind_t exactmask = DNS_ZTFIND_NOEXACT | DNS_ZTFIND_EXACT;
dns_ztfind_t exactopts = options & exactmask;
dns_qpchain_t chain;
REQUIRE(VALID_ZT(zt));
REQUIRE(exactopts != exactmask);
dns_qpmulti_query(zt->multi, &qpr);
if (exactopts == DNS_ZTFIND_EXACT) {
result = dns_qp_getname(&qpr, name, &pval, NULL);
} else if (exactopts == DNS_ZTFIND_NOEXACT) {
result = dns_qp_findname_ancestor(
&qpr, name, DNS_QPFIND_NOEXACT, &pval, NULL);
} else {
result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
result = dns_qp_lookup(&qpr, name, NULL, NULL, &chain, &pval, NULL);
if (exactopts == DNS_ZTFIND_EXACT && result == DNS_R_PARTIALMATCH) {
result = ISC_R_NOTFOUND;
} else if (exactopts == DNS_ZTFIND_NOEXACT && result == ISC_R_SUCCESS) {
/* get pval from the previous chain link */
int len = dns_qpchain_length(&chain);
if (len >= 2) {
dns_qpchain_node(&chain, len - 2, NULL, &pval, NULL);
result = DNS_R_PARTIALMATCH;
} else {
result = ISC_R_NOTFOUND;
}
}
dns_qpread_destroy(zt->multi, &qpr);
@@ -514,7 +520,7 @@ dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub,
dns_qpmulti_query(zt->multi, &qpr);
dns_qpiter_init(&qpr, &qpi);
while (dns_qpiter_next(&qpi, &zone, NULL) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&qpi, NULL, &zone, NULL) == ISC_R_SUCCESS) {
result = action(zone, uap);
if (tresult == ISC_R_SUCCESS) {
tresult = result;
+6 -11
View File
@@ -204,8 +204,6 @@ main(int argc, char **argv) {
dns_name_t *name = NULL;
size_t i = 0, n = 0;
char buf[BUFSIZ];
void *pval = NULL;
uint32_t ival;
if (argc != 2) {
usage();
@@ -228,12 +226,10 @@ main(int argc, char **argv) {
start = isc_time_monotonic();
for (i = 0;; i++) {
if (dns_qpiter_next(&it, &pval, &ival) != ISC_R_SUCCESS) {
name = dns_fixedname_initname(&items[i]);
if (dns_qpiter_next(&it, name, NULL, NULL) != ISC_R_SUCCESS) {
break;
}
name = dns_fixedname_initname(&items[i]);
name_from_smallname(name, pval, ival);
}
stop = isc_time_monotonic();
@@ -254,12 +250,11 @@ main(int argc, char **argv) {
start = isc_time_monotonic();
for (i = 0; i < n; i++) {
name = dns_fixedname_name(&items[i]);
dns_qp_findname_ancestor(qp, name, 0, NULL, NULL);
dns_qp_lookup(qp, name, 0, NULL, NULL, NULL, NULL);
}
stop = isc_time_monotonic();
snprintf(buf, sizeof(buf),
"look up %zd names (dns_qp_findname_ancestor):", n);
snprintf(buf, sizeof(buf), "look up %zd names (dns_qp_lookup):", n);
printf("%-57s%7.3fsec\n", buf, (stop - start) / (double)NS_PER_SEC);
start = isc_time_monotonic();
@@ -280,12 +275,12 @@ main(int argc, char **argv) {
++search->ndata[1];
}
dns_qp_findname_ancestor(qp, search, 0, NULL, NULL);
dns_qp_lookup(qp, search, 0, NULL, NULL, NULL, NULL);
}
stop = isc_time_monotonic();
snprintf(buf, sizeof(buf),
"look up %zd wrong names (dns_qp_findname_ancestor):", n);
"look up %zd wrong names (dns_qp_lookup):", n);
printf("%-57s%7.3fsec\n", buf, (stop - start) / (double)NS_PER_SEC);
isc_mem_cput(mctx, items, n, sizeof(dns_fixedname_t));
+346 -45
View File
@@ -44,6 +44,11 @@ ISC_RUN_TEST_IMPL(qpkey_name) {
uint8_t key[512];
size_t len;
} testcases[] = {
{
.namestr = "",
.key = { 0x02 },
.len = 0,
},
{
.namestr = ".",
.key = { 0x02, 0x02 },
@@ -54,6 +59,16 @@ ISC_RUN_TEST_IMPL(qpkey_name) {
.key = { 0x03, 0x03, 0x02, 0x02 },
.len = 3,
},
{
.namestr = "com",
.key = { 0x16, 0x22, 0x20, 0x02 },
.len = 4,
},
{
.namestr = "com.",
.key = { 0x02, 0x16, 0x22, 0x20, 0x02 },
.len = 5,
},
{
.namestr = "example.com.",
.key = { 0x02, 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14,
@@ -80,15 +95,21 @@ ISC_RUN_TEST_IMPL(qpkey_name) {
dns_fixedname_t fn1, fn2;
dns_name_t *in = NULL, *out = NULL;
dns_test_namefromstring(testcases[i].namestr, &fn1);
in = dns_fixedname_name(&fn1);
in = dns_fixedname_initname(&fn1);
if (testcases[i].len != 0) {
dns_test_namefromstring(testcases[i].namestr, &fn1);
}
len = dns_qpkey_fromname(key, in);
assert_int_equal(testcases[i].len, len);
assert_memory_equal(testcases[i].key, key, len);
/* also check key correctness for empty name */
if (len == 0) {
assert_int_equal(testcases[i].key[0], ((char *)key)[0]);
}
out = dns_fixedname_initname(&fn2);
qp_test_keytoname(key, len, out);
dns_qpkey_toname(key, len, out);
assert_true(dns_name_equal(in, out));
}
}
@@ -178,13 +199,21 @@ const dns_qpmethods_t qpiter_methods = {
ISC_RUN_TEST_IMPL(qpiter) {
dns_qp_t *qp = NULL;
uint32_t item[ITER_ITEMS] = { 0 };
uint32_t order[ITER_ITEMS] = { 0 };
dns_qpiter_t qpi;
int inserted, n;
uint32_t ival;
void *pval = NULL;
dns_qp_create(mctx, &qpiter_methods, item, &qp);
for (size_t tests = 0; tests < 1234; tests++) {
uint32_t ival = isc_random_uniform(ITER_ITEMS - 1) + 1;
void *pval = &item[ival];
ival = isc_random_uniform(ITER_ITEMS - 1) + 1;
pval = &item[ival];
item[ival] = ival;
inserted = n = 0;
/* randomly insert or remove */
dns_qpkey_t key;
size_t len = qpiter_makekey(key, item, pval, ival);
@@ -199,12 +228,14 @@ ISC_RUN_TEST_IMPL(qpiter) {
/* check that we see only valid items in the correct order */
uint32_t prev = 0;
dns_qpiter_t qpi;
dns_qpiter_init(qp, &qpi);
while (dns_qpiter_next(&qpi, &pval, &ival) == ISC_R_SUCCESS) {
while (dns_qpiter_next(&qpi, NULL, &pval, &ival) ==
ISC_R_SUCCESS)
{
assert_in_range(ival, prev + 1, ITER_ITEMS - 1);
assert_int_equal(ival, item[ival]);
assert_ptr_equal(pval, &item[ival]);
order[inserted++] = ival;
item[ival] = ~ival;
prev = ival;
}
@@ -216,7 +247,62 @@ ISC_RUN_TEST_IMPL(qpiter) {
item[ival] = ival;
}
}
/* now iterate backward and check correctness */
n = inserted;
while (dns_qpiter_prev(&qpi, NULL, NULL, &ival) ==
ISC_R_SUCCESS)
{
assert_int_equal(ival, order[--n]);
}
assert_int_equal(n, 0);
/* ...and forward again */
while (dns_qpiter_next(&qpi, NULL, NULL, &ival) ==
ISC_R_SUCCESS)
{
assert_int_equal(ival, order[n++]);
}
assert_int_equal(n, inserted);
/*
* if there are enough items inserted, try going
* forward a few steps, then back to the start,
* to confirm we can change directions while iterating.
*/
if (tests > 3) {
assert_int_equal(
dns_qpiter_next(&qpi, NULL, NULL, &ival),
ISC_R_SUCCESS);
assert_int_equal(ival, order[0]);
assert_int_equal(
dns_qpiter_next(&qpi, NULL, NULL, &ival),
ISC_R_SUCCESS);
assert_int_equal(ival, order[1]);
assert_int_equal(
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
ISC_R_SUCCESS);
assert_int_equal(ival, order[0]);
assert_int_equal(
dns_qpiter_next(&qpi, NULL, NULL, &ival),
ISC_R_SUCCESS);
assert_int_equal(ival, order[1]);
assert_int_equal(
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
ISC_R_SUCCESS);
assert_int_equal(ival, order[0]);
assert_int_equal(
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
ISC_R_NOMORE);
}
}
dns_qp_destroy(&qp);
}
@@ -249,7 +335,6 @@ const dns_qpmethods_t string_methods = {
struct check_partialmatch {
const char *query;
dns_qpfind_t options;
isc_result_t result;
const char *found;
};
@@ -258,19 +343,47 @@ static void
check_partialmatch(dns_qp_t *qp, struct check_partialmatch check[]) {
for (int i = 0; check[i].query != NULL; i++) {
isc_result_t result;
dns_fixedname_t fixed;
dns_name_t *name = dns_fixedname_name(&fixed);
dns_fixedname_t fn1, fn2;
dns_name_t *name = dns_fixedname_initname(&fn1);
dns_name_t *foundname = dns_fixedname_initname(&fn2);
void *pval = NULL;
dns_test_namefromstring(check[i].query, &fn1);
result = dns_qp_lookup(qp, name, foundname, NULL, NULL, &pval,
NULL);
#if 0
fprintf(stderr, "%s %u %s %s\n", check[i].query,
check[i].options, isc_result_totext(check[i].result),
fprintf(stderr, "%s %s (expected %s) "
"value \"%s\" (expected \"%s\")\n",
check[i].query,
isc_result_totext(result),
isc_result_totext(check[i].result), (char *)pval,
check[i].found);
#endif
dns_test_namefromstring(check[i].query, &fixed);
result = dns_qp_findname_ancestor(qp, name, check[i].options,
&pval, NULL);
assert_int_equal(result, check[i].result);
if (result == ISC_R_SUCCESS) {
assert_true(dns_name_equal(name, foundname));
} else if (result == DNS_R_PARTIALMATCH) {
/*
* there are cases where we may have passed a
* query name that was relative to the zone apex,
* and gotten back an absolute name from the
* partial match. it's also possible for an
* absolute query to get a partial match on a
* node that had an empty name. in these cases,
* sanity checking the relations between name
* and foundname can trigger an assertion, so
* let's just skip them.
*/
if (dns_name_isabsolute(name) ==
dns_name_isabsolute(foundname))
{
assert_false(dns_name_equal(name, foundname));
assert_true(
dns_name_issubdomain(name, foundname));
}
}
if (check[i].found == NULL) {
assert_null(pval);
} else {
@@ -291,6 +404,7 @@ insert_str(dns_qp_t *qp, const char *str) {
ISC_RUN_TEST_IMPL(partialmatch) {
isc_result_t result;
dns_qp_t *qp = NULL;
int i = 0;
dns_qp_create(mctx, &string_methods, NULL, &qp);
@@ -302,32 +416,28 @@ ISC_RUN_TEST_IMPL(partialmatch) {
"fooo.bar.", "web.foo.bar.", ".", "",
};
int i = 0;
/*
* omit the root node for now, otherwise we'll get "partial match"
* results when we want "not found".
*/
while (insert[i][0] != '.') {
insert_str(qp, insert[i++]);
}
static struct check_partialmatch check1[] = {
{ "a.b.", 0, ISC_R_SUCCESS, "a.b." },
{ "a.b.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "b." },
{ "b.c.", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
{ "bar.", 0, ISC_R_NOTFOUND, NULL },
{ "f.bar.", 0, ISC_R_NOTFOUND, NULL },
{ "foo.bar.", 0, ISC_R_SUCCESS, "foo.bar." },
{ "foo.bar.", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
{ "foooo.bar.", 0, ISC_R_NOTFOUND, NULL },
{ "w.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
{ "www.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
{ "web.foo.bar.", 0, ISC_R_SUCCESS, "web.foo.bar." },
{ "webby.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
{ "my.web.foo.bar.", 0, DNS_R_PARTIALMATCH, "web.foo.bar." },
{ "web.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
"foo.bar." },
{ "my.web.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
"web.foo.bar." },
{ "my.other.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
"foo.bar." },
{ NULL, 0, 0, NULL },
{ "a.b.", ISC_R_SUCCESS, "a.b." },
{ "b.c.", ISC_R_NOTFOUND, NULL },
{ "bar.", ISC_R_NOTFOUND, NULL },
{ "f.bar.", ISC_R_NOTFOUND, NULL },
{ "foo.bar.", ISC_R_SUCCESS, "foo.bar." },
{ "foooo.bar.", ISC_R_NOTFOUND, NULL },
{ "w.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
{ "www.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
{ "web.foo.bar.", ISC_R_SUCCESS, "web.foo.bar." },
{ "webby.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
{ "my.web.foo.bar.", DNS_R_PARTIALMATCH, "web.foo.bar." },
{ "my.other.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
{ NULL, 0, NULL },
};
check_partialmatch(qp, check1);
@@ -336,34 +446,225 @@ ISC_RUN_TEST_IMPL(partialmatch) {
insert_str(qp, insert[i++]);
static struct check_partialmatch check2[] = {
{ "b.c.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "." },
{ "bar.", 0, DNS_R_PARTIALMATCH, "." },
{ "foo.bar.", 0, ISC_R_SUCCESS, "foo.bar." },
{ "foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "." },
{ NULL, 0, 0, NULL },
{ "b.c.", DNS_R_PARTIALMATCH, "." },
{ "bar.", DNS_R_PARTIALMATCH, "." },
{ "foo.bar.", ISC_R_SUCCESS, "foo.bar." },
{ "bar", ISC_R_NOTFOUND, NULL },
{ NULL, 0, NULL },
};
check_partialmatch(qp, check2);
/* what if entries in the trie are relative to the zone apex? */
/*
* what if entries in the trie are relative to the zone apex
* and there's no root node?
*/
dns_qpkey_t rootkey = { SHIFT_NOBYTE };
result = dns_qp_deletekey(qp, rootkey, 1, NULL, NULL);
assert_int_equal(result, ISC_R_SUCCESS);
check_partialmatch(qp, (struct check_partialmatch[]){
{ "bar", ISC_R_NOTFOUND, NULL },
{ "bar.", ISC_R_NOTFOUND, NULL },
{ NULL, 0, NULL },
});
/* what if there's a root node with an empty key? */
INSIST(insert[i][0] == '\0');
insert_str(qp, insert[i++]);
check_partialmatch(qp, (struct check_partialmatch[]){
{ "bar", 0, DNS_R_PARTIALMATCH, "" },
{ "bar.", 0, DNS_R_PARTIALMATCH, "" },
{ NULL, 0, 0, NULL },
{ "bar", DNS_R_PARTIALMATCH, "" },
{ "bar.", DNS_R_PARTIALMATCH, "" },
{ NULL, 0, NULL },
});
dns_qp_destroy(&qp);
}
struct check_qpchain {
const char *query;
isc_result_t result;
unsigned int length;
const char *names[10];
};
static void
check_qpchain(dns_qp_t *qp, struct check_qpchain check[]) {
for (int i = 0; check[i].query != NULL; i++) {
isc_result_t result;
dns_fixedname_t fn1;
dns_name_t *name = dns_fixedname_initname(&fn1);
dns_qpchain_t chain;
dns_qpchain_init(qp, &chain);
dns_test_namefromstring(check[i].query, &fn1);
result = dns_qp_lookup(qp, name, NULL, NULL, &chain, NULL,
NULL);
#if 0
fprintf(stderr, "%s %s (expected %s), "
"len %d (expected %d)\n", check[i].query,
isc_result_totext(result),
isc_result_totext(check[i].result),
dns_qpchain_length(&chain), check[i].length);
#endif
assert_int_equal(result, check[i].result);
assert_int_equal(dns_qpchain_length(&chain), check[i].length);
for (unsigned int j = 0; j < check[i].length; j++) {
dns_fixedname_t fn2, fn3;
dns_name_t *expected = dns_fixedname_initname(&fn2);
dns_name_t *found = dns_fixedname_initname(&fn3);
dns_test_namefromstring(check[i].names[j], &fn2);
dns_qpchain_node(&chain, j, found, NULL, NULL);
#if 0
char nb[DNS_NAME_FORMATSIZE];
dns_name_format(found, nb, sizeof(nb));
fprintf(stderr, "got %s, expected %s\n", nb,
check[i].names[j]);
#endif
assert_true(dns_name_equal(found, expected));
}
}
}
ISC_RUN_TEST_IMPL(qpchain) {
dns_qp_t *qp = NULL;
const char insert[][16] = { ".", "a.", "b.",
"c.b.a.", "e.d.c.b.a.", "c.b.b.",
"c.d.", "a.b.c.d.", "a.b.c.d.e.",
"b.a.", "x.k.c.d.", "" };
int i = 0;
dns_qp_create(mctx, &string_methods, NULL, &qp);
while (insert[i][0] != '\0') {
insert_str(qp, insert[i++]);
}
static struct check_qpchain check1[] = {
{ "b.", ISC_R_SUCCESS, 2, { ".", "b." } },
{ "b.a.", ISC_R_SUCCESS, 3, { ".", "a.", "b.a." } },
{ "c.", DNS_R_PARTIALMATCH, 1, { "." } },
{ "e.d.c.b.a.",
ISC_R_SUCCESS,
5,
{ ".", "a.", "b.a.", "c.b.a.", "e.d.c.b.a." } },
{ "a.b.c.d.", ISC_R_SUCCESS, 3, { ".", "c.d.", "a.b.c.d." } },
{ "b.c.d.", DNS_R_PARTIALMATCH, 2, { ".", "c.d." } },
{ NULL, 0, 0, { NULL } },
};
check_qpchain(qp, check1);
dns_qp_destroy(&qp);
}
struct check_predecessors {
const char *query;
const char *predecessor;
isc_result_t result;
};
static void
check_predecessors(dns_qp_t *qp, struct check_predecessors check[]) {
isc_result_t result;
dns_fixedname_t fn1, fn2;
dns_name_t *name = dns_fixedname_initname(&fn1);
dns_name_t *pred = dns_fixedname_initname(&fn2);
for (int i = 0; check[i].query != NULL; i++) {
char *predname = NULL;
dns_test_namefromstring(check[i].query, &fn1);
result = dns_qp_lookup(qp, name, NULL, pred, NULL, NULL, NULL);
#if 0
fprintf(stderr, "%s: expected %s got %s\n", check[i].query,
isc_result_totext(check[i].result),
isc_result_totext(result));
#endif
assert_int_equal(result, check[i].result);
result = dns_name_tostring(pred, &predname, mctx);
#if 0
fprintf(stderr, "... expected predecessor %s got %s\n",
check[i].predecessor, predname);
#endif
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(predname, check[i].predecessor);
isc_mem_free(mctx, predname);
}
}
ISC_RUN_TEST_IMPL(predecessors) {
dns_qp_t *qp = NULL;
const char insert[][16] = {
"a.", "b.", "c.b.a.", "e.d.c.b.a.",
"c.b.b.", "c.d.", "a.b.c.d.", "a.b.c.d.e.",
"b.a.", "x.k.c.d.", ""
};
int i = 0;
dns_qp_create(mctx, &string_methods, NULL, &qp);
while (insert[i][0] != '\0') {
insert_str(qp, insert[i++]);
}
/* first check: no root label in the database */
static struct check_predecessors check1[] = {
{ ".", "a.b.c.d.e.", ISC_R_NOTFOUND },
{ "a.", "a.b.c.d.e.", ISC_R_SUCCESS },
{ "b.a.", "a.", ISC_R_SUCCESS },
{ "b.", "e.d.c.b.a.", ISC_R_SUCCESS },
{ "aaa.a.", "a.", DNS_R_PARTIALMATCH },
{ "ddd.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH },
{ "d.c.", "c.b.b.", ISC_R_NOTFOUND },
{ "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH },
{ "a.b.c.e.f.", "a.b.c.d.e.", ISC_R_NOTFOUND },
{ "z.y.x.", "a.b.c.d.e.", ISC_R_NOTFOUND },
{ "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH },
{ "z.z.z.z.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH },
{ "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH },
{ "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH },
{ "0.b.c.d.e.", "x.k.c.d.", ISC_R_NOTFOUND },
{ "b.d.", "c.b.b.", ISC_R_NOTFOUND },
{ NULL, NULL, 0 }
};
check_predecessors(qp, check1);
/* second check: add a root label and try again */
const char root[16] = ".";
insert_str(qp, root);
static struct check_predecessors check2[] = {
{ ".", "a.b.c.d.e.", ISC_R_SUCCESS },
{ "a.", ".", ISC_R_SUCCESS },
{ "b.a.", "a.", ISC_R_SUCCESS },
{ "b.", "e.d.c.b.a.", ISC_R_SUCCESS },
{ "aaa.a.", "a.", DNS_R_PARTIALMATCH },
{ "ddd.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH },
{ "d.c.", "c.b.b.", DNS_R_PARTIALMATCH },
{ "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH },
{ "a.b.c.e.f.", "a.b.c.d.e.", DNS_R_PARTIALMATCH },
{ "z.y.x.", "a.b.c.d.e.", DNS_R_PARTIALMATCH },
{ "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH },
{ "z.z.z.z.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH },
{ "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH },
{ "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH },
{ "0.b.c.d.e.", "x.k.c.d.", DNS_R_PARTIALMATCH },
{ NULL, NULL, 0 }
};
check_predecessors(qp, check2);
dns_qp_destroy(&qp);
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY(qpkey_name)
ISC_TEST_ENTRY(qpkey_sort)
ISC_TEST_ENTRY(qpiter)
ISC_TEST_ENTRY(partialmatch)
ISC_TEST_ENTRY(qpchain)
ISC_TEST_ENTRY(predecessors)
ISC_TEST_LIST_END
ISC_TEST_MAIN
-7
View File
@@ -29,13 +29,6 @@ qp_test_bittoascii(uint8_t bit);
const char *
qp_test_keytoascii(dns_qpkey_t key, size_t len);
/*
* Convert a trie lookup key back into a DNS name. Unlike the previous
* functions, this is a complete inverse of dns_qpkey_fromname().
*/
void
qp_test_keytoname(const dns_qpkey_t key, size_t len, dns_name_t *name);
/*
* The maximum height of the trie
*/
+38 -108
View File
@@ -37,7 +37,7 @@
*/
uint8_t
qp_test_bittoascii(qp_shift_t bit) {
qp_test_bittoascii(dns_qpshift_t bit) {
uint8_t byte = dns_qp_byte_for_bit[bit];
if (bit == SHIFT_NOBYTE) {
return ('.');
@@ -61,94 +61,20 @@ qp_test_keytoascii(dns_qpkey_t key, size_t len) {
return ((const char *)key);
}
void
qp_test_keytoname(const dns_qpkey_t key, size_t keylen, dns_name_t *name) {
size_t locs[DNS_NAME_MAXLABELS];
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 <= keylen; offset++) {
INSIST(key[offset] >= SHIFT_NOBYTE &&
key[offset] < SHIFT_OFFSET);
INSIST(loc < DNS_NAME_MAXLABELS);
if (qpkey_bit(key, keylen, offset) == SHIFT_NOBYTE) {
if (qpkey_bit(key, keylen, offset + 1) == SHIFT_NOBYTE)
{
locs[loc] = offset + 1;
goto scanned;
}
locs[loc++] = offset + 1;
} else if (offset == 0) {
/* This happens for a relative name */
locs[loc++] = offset;
}
}
UNREACHABLE();
scanned:
/*
* 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 bit = qpkey_bit(key, keylen, offset);
uint8_t byte = dns_qp_byte_for_bit[bit];
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) {
getheight(dns_qp_t *qp, dns_qpnode_t *n) {
if (node_tag(n) == LEAF_TAG) {
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++) {
dns_qpnode_t *twigs = branch_twigs(qp, n);
dns_qpweight_t size = branch_twigs_size(n);
for (dns_qpweight_t pos = 0; pos < size; pos++) {
size_t height = getheight(qp, &twigs[pos]);
max_height = ISC_MAX(max_height, height);
}
@@ -157,20 +83,20 @@ getheight(dns_qp_t *qp, qp_node_t *n) {
size_t
qp_test_getheight(dns_qp_t *qp) {
qp_node_t *root = get_root(qp);
dns_qpnode_t *root = get_root(qp);
return (root == NULL ? 0 : getheight(qp, root));
}
static size_t
maxkeylen(dns_qp_t *qp, qp_node_t *n) {
maxkeylen(dns_qp_t *qp, dns_qpnode_t *n) {
if (node_tag(n) == LEAF_TAG) {
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++) {
dns_qpnode_t *twigs = branch_twigs(qp, n);
dns_qpweight_t size = branch_twigs_size(n);
for (dns_qpweight_t pos = 0; pos < size; pos++) {
size_t len = maxkeylen(qp, &twigs[pos]);
max_len = ISC_MAX(max_len, len);
}
@@ -179,7 +105,7 @@ maxkeylen(dns_qp_t *qp, qp_node_t *n) {
size_t
qp_test_maxkeylen(dns_qp_t *qp) {
qp_node_t *root = get_root(qp);
dns_qpnode_t *root = get_root(qp);
return (root == NULL ? 0 : maxkeylen(qp, root));
}
@@ -232,7 +158,7 @@ qp_test_dumpqp(dns_qp_t *qp) {
void
qp_test_dumpmulti(dns_qpmulti_t *multi) {
dns_qpreader_t qpr;
qp_node_t *reader = rcu_dereference(multi->reader);
dns_qpnode_t *reader = rcu_dereference(multi->reader);
dns_qpmulti_t *whence = unpack_reader(&qpr, reader);
dumpqp(&multi->writer, "qpmulti->writer");
printf("qpmulti->reader %p root_ref %u %u:%u base %p\n", reader,
@@ -250,10 +176,10 @@ qp_test_dumpmulti(dns_qpmulti_t *multi) {
void
qp_test_dumpchunks(dns_qp_t *qp) {
qp_cell_t used = 0;
qp_cell_t free = 0;
dns_qpcell_t used = 0;
dns_qpcell_t free = 0;
dumpqp(qp, "qp");
for (qp_chunk_t c = 0; c < qp->chunk_max; c++) {
for (dns_qpchunk_t c = 0; c < qp->chunk_max; c++) {
printf("qp %p chunk %u base %p "
"used %u free %u immutable %u discounted %u\n",
qp, c, qp->base->ptr[c], qp->usage[c].used,
@@ -270,11 +196,11 @@ void
qp_test_dumptrie(dns_qpreadable_t qpr) {
dns_qpreader_t *qp = dns_qpreader(qpr);
struct {
qp_ref_t ref;
qp_shift_t max, pos;
dns_qpref_t ref;
dns_qpshift_t max, pos;
} stack[512];
size_t sp = 0;
qp_cell_t leaf_count = 0;
dns_qpcell_t leaf_count = 0;
/*
* fake up a sentinel stack entry corresponding to the root
@@ -285,7 +211,7 @@ qp_test_dumptrie(dns_qpreadable_t qpr) {
stack[sp].max = 0;
stack[sp].pos = 0;
qp_node_t *n = get_root(qp);
dns_qpnode_t *n = get_root(qp);
if (n == NULL) {
printf("%p EMPTY\n", n);
fflush(stdout);
@@ -296,15 +222,15 @@ qp_test_dumptrie(dns_qpreadable_t qpr) {
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);
dns_qpref_t ref = branch_twigs_ref(n);
dns_qpweight_t max = branch_twigs_size(n);
dns_qpnode_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++)
for (dns_qpshift_t bit = SHIFT_NOBYTE;
bit < SHIFT_OFFSET; bit++)
{
if (branch_has_twig(n, bit)) {
bits[len++] = bit;
@@ -337,17 +263,19 @@ qp_test_dumptrie(dns_qpreadable_t qpr) {
--sp;
}
n = ref_ptr(qp, stack[sp].ref) + stack[sp].pos;
stack[sp].pos++;
fprintf(stderr, "pos %d/%d, ref+%d\n", stack[sp].pos,
stack[sp].max, stack[sp].pos - 1);
n = ref_ptr(qp, stack[sp].ref) + stack[sp].pos - 1;
}
}
static void
dumpdot_name(qp_node_t *n) {
dumpdot_name(dns_qpnode_t *n) {
if (n == NULL) {
printf("empty");
} else if (is_branch(n)) {
qp_ref_t ref = branch_twigs_ref(n);
dns_qpref_t ref = branch_twigs_ref(n);
printf("c%dn%d", ref_chunk(ref), ref_cell(ref));
} else {
printf("v%p", leaf_pval(n));
@@ -355,7 +283,7 @@ dumpdot_name(qp_node_t *n) {
}
static void
dumpdot_twig(dns_qp_t *qp, qp_node_t *n) {
dumpdot_twig(dns_qp_t *qp, dns_qpnode_t *n) {
if (n == NULL) {
printf("empty [shape=oval, label=\"\\N EMPTY\"];\n");
} else if (is_branch(n)) {
@@ -363,7 +291,9 @@ dumpdot_twig(dns_qp_t *qp, qp_node_t *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++) {
for (dns_qpshift_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),
@@ -373,17 +303,17 @@ dumpdot_twig(dns_qp_t *qp, qp_node_t *n) {
}
printf("}}\"];\n");
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
dns_qpnode_t *twigs = branch_twigs(qp, n);
dns_qpweight_t size = branch_twigs_size(n);
for (qp_weight_t pos = 0; pos < size; pos++) {
for (dns_qpweight_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++) {
for (dns_qpweight_t pos = 0; pos < size; pos++) {
dumpdot_twig(qp, &twigs[pos]);
}
@@ -399,7 +329,7 @@ dumpdot_twig(dns_qp_t *qp, qp_node_t *n) {
void
qp_test_dumpdot(dns_qp_t *qp) {
REQUIRE(QP_VALID(qp));
qp_node_t *n = get_root(qp);
dns_qpnode_t *n = get_root(qp);
printf("strict digraph {\nrankdir = \"LR\"; ranksep = 1.0;\n");
printf("ROOT [shape=point]; ROOT -> ");
dumpdot_name(n);