add an iterator argument to dns_qp_lookup()
the 'predecessor' argument to dns_qp_lookup() turns out not to be sufficient for our needs: the predecessor node in a QP database could have become "empty" (for the current version) because of an update or because cache data expired, and in that case the caller would have to iterate more than one step back to find the predecessor node that it needs. it may also be necessary for a caller to iterate forward, in order to determine whether a node has any children. for both of these reasons, we now replace the 'predecessor' argument with an 'iter' argument. if set, this points to memory with enough space for a dns_qpiter object. when an exact match is found by the lookup, the iterator will be pointing to the matching node. if not, it will be pointing to the lexical predecessor of the nae that was searched for. a dns_qpiter_current() method has been added for examining the current value of the iterator without moving it in either direction.
This commit is contained in:
@@ -528,8 +528,8 @@ dns_qp_getname(dns_qpreadable_t qpr, const dns_name_t *name, void **pval_r,
|
||||
|
||||
isc_result_t
|
||||
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);
|
||||
dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain,
|
||||
void **pval_r, uint32_t *ival_r);
|
||||
/*%<
|
||||
* Look up a leaf in a qp-trie that is equal to, or an ancestor domain of,
|
||||
* 'name'.
|
||||
@@ -547,9 +547,9 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
* 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.
|
||||
* If 'iter' is not NULL, it will be updated to point to a QP iterator
|
||||
* which is pointed at the searched-for name if it exists in the trie,
|
||||
* or the closest predecessor if it doesn't.
|
||||
*
|
||||
* 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
|
||||
@@ -559,7 +559,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
* \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.
|
||||
* buffer and offset space available, or is NULL
|
||||
*
|
||||
* Returns:
|
||||
* \li ISC_R_SUCCESS if an exact match was found
|
||||
@@ -666,6 +666,24 @@ dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
|
||||
* \li ISC_R_NOMORE otherwise
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_qpiter_current(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
|
||||
uint32_t *ival_r);
|
||||
/*%<
|
||||
* Sets the values of `name`, `pval_r` and `ival_r` to those at the
|
||||
* node currently pointed to by `qpi`, but without moving the iterator
|
||||
* in either direction. If the iterator is not currently pointed at a
|
||||
* leaf node, ISC_R_FAILURE is returned.
|
||||
* Requires:
|
||||
*
|
||||
* \li `qpi` is a pointer to a valid qp iterator
|
||||
*
|
||||
* Returns:
|
||||
* \li ISC_R_SUCCESS if a leaf was found and pval_r and ival_r were set
|
||||
* \li ISC_R_FAILURE if the iterator is not initialized or not pointing
|
||||
* at a leaf node
|
||||
*/
|
||||
|
||||
void
|
||||
dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain);
|
||||
/*%<
|
||||
@@ -673,7 +691,7 @@ dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain);
|
||||
*
|
||||
* Requires:
|
||||
* \li `qpr` is a pointer to a valid qp-trie
|
||||
* \li `chain` is not NULL.
|
||||
* \li `chain` is not NULL
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
@@ -682,7 +700,7 @@ 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.
|
||||
* \li `chain` is a pointer to an initialized QP chain object
|
||||
*/
|
||||
|
||||
void
|
||||
@@ -695,8 +713,8 @@ dns_qpchain_node(dns_qpchain_t *chain, unsigned int level, dns_name_t *name,
|
||||
* are not null.
|
||||
*
|
||||
* Requires:
|
||||
* \li `chain` is a pointer to an initialized QP chain object.
|
||||
* \li `level` is less than `chain->len`.
|
||||
* \li `chain` is a pointer to an initialized QP chain object
|
||||
* \li `level` is less than `chain->len`
|
||||
*/
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
75
lib/dns/qp.c
75
lib/dns/qp.c
@@ -1948,6 +1948,24 @@ dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
|
||||
return (iterate(false, qpi, name, pval_r, ival_r));
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_qpiter_current(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
|
||||
uint32_t *ival_r) {
|
||||
dns_qpnode_t *node = NULL;
|
||||
|
||||
REQUIRE(QPITER_VALID(qpi));
|
||||
|
||||
node = qpi->stack[qpi->sp];
|
||||
if (node == NULL || is_branch(node)) {
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
SET_IF_NOT_NULL(pval_r, leaf_pval(node));
|
||||
SET_IF_NOT_NULL(ival_r, leaf_ival(node));
|
||||
maybe_set_name(qpi->qp, node, name);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* search
|
||||
@@ -2022,8 +2040,8 @@ prevleaf(dns_qpiter_t *it) {
|
||||
|
||||
isc_result_t
|
||||
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) {
|
||||
dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain,
|
||||
void **pval_r, uint32_t *ival_r) {
|
||||
dns_qpreader_t *qp = dns_qpreader(qpr);
|
||||
dns_qpkey_t search, found;
|
||||
size_t searchlen, foundlen;
|
||||
@@ -2032,6 +2050,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
dns_qpchain_t oc;
|
||||
dns_qpiter_t it;
|
||||
bool matched = true;
|
||||
bool getpred = true;
|
||||
|
||||
REQUIRE(QP_VALID(qp));
|
||||
REQUIRE(foundname == NULL || ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
|
||||
@@ -2041,14 +2060,18 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
if (chain == NULL) {
|
||||
chain = &oc;
|
||||
}
|
||||
if (iter == NULL) {
|
||||
iter = ⁢
|
||||
getpred = false;
|
||||
}
|
||||
dns_qpchain_init(qp, chain);
|
||||
dns_qpiter_init(qp, &it);
|
||||
dns_qpiter_init(qp, iter);
|
||||
|
||||
n = get_root(qp);
|
||||
if (n == NULL) {
|
||||
return (ISC_R_NOTFOUND);
|
||||
}
|
||||
it.stack[0] = n;
|
||||
iter->stack[0] = n;
|
||||
|
||||
/*
|
||||
* Like `dns_qp_insert()`, we must find a leaf. However, we don't make a
|
||||
@@ -2093,11 +2116,11 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
* the loop.
|
||||
*/
|
||||
n = branch_twig_ptr(qp, n, bit);
|
||||
} else if (predecessor != NULL) {
|
||||
} else if (getpred) {
|
||||
/*
|
||||
* this branch is a dead end, but the caller wants
|
||||
* the predecessor to the name we were searching
|
||||
* for, so let's go find that.
|
||||
* this branch is a dead end, but the caller
|
||||
* passed us an iterator: position it at the
|
||||
* predecessor node.
|
||||
*/
|
||||
dns_qpweight_t pos = branch_twig_pos(n, bit);
|
||||
if (pos == 0) {
|
||||
@@ -2106,8 +2129,8 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
* the key we wanted, so we step back to
|
||||
* the predecessor using the iterator.
|
||||
*/
|
||||
prevleaf(&it);
|
||||
n = it.stack[it.sp];
|
||||
prevleaf(iter);
|
||||
n = iter->stack[iter->sp];
|
||||
} else {
|
||||
/*
|
||||
* the name we want would've been between
|
||||
@@ -2119,7 +2142,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
n = twigs + pos - 1;
|
||||
while (is_branch(n)) {
|
||||
prefetch_twigs(qp, n);
|
||||
it.stack[++it.sp] = n;
|
||||
iter->stack[++iter->sp] = n;
|
||||
pos = branch_twigs_size(n) - 1;
|
||||
n = ref_ptr(qp,
|
||||
branch_twigs_ref(n) + pos);
|
||||
@@ -2143,7 +2166,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
}
|
||||
}
|
||||
|
||||
it.stack[++it.sp] = n;
|
||||
iter->stack[++iter->sp] = n;
|
||||
}
|
||||
|
||||
/* do the keys differ, and if so, where? */
|
||||
@@ -2151,20 +2174,26 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
offset = qpkey_compare(search, searchlen, found, foundlen);
|
||||
|
||||
/*
|
||||
* if we've been asked to return the predecessor name, we
|
||||
* work that out here.
|
||||
* if we've been passed an iterator, we want it to point
|
||||
* at the matching name in the case of an exact match, or at
|
||||
* the predecessor name for a non-exact match.
|
||||
*
|
||||
* if 'matched' is true, the search ended at a leaf. it's either
|
||||
* an exact match or the immediate successor of the searched-for
|
||||
* name, and in either case, we can use the qpiter stack we've
|
||||
* constructed to step back to the predecessor. if 'matched' is
|
||||
* false, then the search failed at a branch node, and we would
|
||||
* have already found the predecessor.
|
||||
* if 'matched' is true, then the search ended at a leaf.
|
||||
* if it was not an exact match, then we're now pointing at
|
||||
* the immediate successor of the searched-for name, and can
|
||||
* use the qpiter stack we've constructed to step back to
|
||||
* the predecessor. if it was an exact match, we don't need to
|
||||
* do anything.
|
||||
*
|
||||
* if 'matched' is false, then the search failed at a branch
|
||||
* node, and we would already have positioned the iterator
|
||||
* at the predecessor.
|
||||
*/
|
||||
if (predecessor != NULL && matched) {
|
||||
prevleaf(&it);
|
||||
if (getpred && matched) {
|
||||
if (offset != QPKEY_EQUAL) {
|
||||
prevleaf(iter);
|
||||
}
|
||||
}
|
||||
maybe_set_name(qp, it.stack[it.sp], predecessor);
|
||||
|
||||
if (offset == QPKEY_EQUAL || offset == foundlen) {
|
||||
SET_IF_NOT_NULL(pval_r, leaf_pval(n));
|
||||
|
||||
@@ -204,6 +204,7 @@ ISC_RUN_TEST_IMPL(qpiter) {
|
||||
int inserted, n;
|
||||
uint32_t ival;
|
||||
void *pval = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
dns_qp_create(mctx, &qpiter_methods, item, &qp);
|
||||
for (size_t tests = 0; tests < 1234; tests++) {
|
||||
@@ -253,15 +254,30 @@ ISC_RUN_TEST_IMPL(qpiter) {
|
||||
while (dns_qpiter_prev(&qpi, NULL, NULL, &ival) ==
|
||||
ISC_R_SUCCESS)
|
||||
{
|
||||
assert_int_equal(ival, order[--n]);
|
||||
--n;
|
||||
|
||||
assert_int_equal(ival, order[n]);
|
||||
|
||||
/* and check current iterator value as well */
|
||||
result = dns_qpiter_current(&qpi, NULL, NULL, &ival);
|
||||
assert_int_equal(result, 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(ival, order[n]);
|
||||
|
||||
/* and check current iterator value as well */
|
||||
result = dns_qpiter_current(&qpi, NULL, NULL, &ival);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(ival, order[n]);
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
assert_int_equal(n, inserted);
|
||||
@@ -575,16 +591,36 @@ check_predecessors(dns_qp_t *qp, struct check_predecessors check[]) {
|
||||
dns_name_t *pred = dns_fixedname_initname(&fn2);
|
||||
|
||||
for (int i = 0; check[i].query != NULL; i++) {
|
||||
dns_qpiter_t it;
|
||||
char *predname = NULL;
|
||||
|
||||
dns_test_namefromstring(check[i].query, &fn1);
|
||||
result = dns_qp_lookup(qp, name, NULL, pred, NULL, NULL, NULL);
|
||||
result = dns_qp_lookup(qp, name, NULL, &it, 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);
|
||||
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* we found an exact match; iterate to find
|
||||
* the predecessor.
|
||||
*/
|
||||
result = dns_qpiter_prev(&it, pred, NULL, NULL);
|
||||
if (result == ISC_R_NOMORE) {
|
||||
result = dns_qpiter_prev(&it, pred, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* we didn't find a match, so the iterator should
|
||||
* already be pointed at the predecessor node.
|
||||
*/
|
||||
result = dns_qpiter_current(&it, pred, NULL, NULL);
|
||||
}
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
result = dns_name_tostring(pred, &predname, mctx);
|
||||
#if 0
|
||||
fprintf(stderr, "... expected predecessor %s got %s\n",
|
||||
|
||||
Reference in New Issue
Block a user