diff --git a/lib/dns/qp.c b/lib/dns/qp.c index 1fd36b91ba..7c04957342 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -2049,6 +2049,141 @@ greatest_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpiter_t *iter) { return (n); } +static inline dns_qpnode_t * +anyleaf(dns_qpreader_t *qp, dns_qpnode_t *n) { + while (is_branch(n)) { + n = branch_twigs(qp, n); + } + return (n); +} + +/* + * If dns_qp_lookup() was 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 the search ended at a leaf, and it was an exact match, the iterator is + * already pointing to the correct node (the compare results in QPKEY_EQUAL), + * and we don't need to do anything else. Otherwise, we have found a leaf node + * that is close by. There are a couple of scenarios: + * - If the search key differs from the found key before the offset point... + * - and the search key at that point is smaller then the found key, the + * predecessor of the searched-for name is somewhere in the left twig of + * the parent node (but not necessarily the direct parent node). + * - and the search key at that point is equal or greater then the found key, + * the found key is the predecessor of the searched-for name. + * - If the search key differs at or after the offset point... + * - and the search key is smaller than the found key, then the iterator + * points to the immediate successor of the search key and we can use the + * qpiter stack to step back one leaf to the predecessor. + * - and the search key is greater than the found key, the found key is the + * the predecessor, and the iterator already points to the correct node + * (so we don't need to do anything anymore). + * + * If the search ended on a branch, we first continue down to the left-most + * (least) leaf node to compare the search key against. + * - If the search key differs from the found key before the offset point... + * - and the search key at that point is smaller then the found key, the + * predecessor of the searched-for name is somewhere in the left twig of + * the parent node (but not necessarily the direct parent node). + * - and the search key at that point is equal or greater then the found key, + * the greatest leaf of the branch is the predecessor of the searched-for + * name. + * - If the search key differs at or after the offset point... + * - and there is a twig for the key at the offset point, the predecessor + * is the greatest leaf node in the twig left of the found twig. + * - and there is no twig for the key at the offset point, the search key + * is smaller than all the leaves on this branch and the predecessor for the + * searched-for name is in the most-right (greatest) leaf in left twig of + * the parent branch. We can use the qpiter stack to step back one leaf to + * the predecessor. + */ +static dns_qpnode_t * +fix_iterator(dns_qpreader_t *qp, dns_qpiter_t *iter, dns_qpnode_t *start, + dns_qpkey_t search, size_t searchlen, dns_qpshift_t bit, + size_t offset) { + dns_qpkey_t found; + size_t foundlen, to; + dns_qpnode_t *n = start; + dns_qpnode_t *leaf = anyleaf(qp, n); + + foundlen = leaf_qpkey(qp, leaf, found); + to = qpkey_compare(search, searchlen, found, foundlen); + if (to == QPKEY_EQUAL) { + return (leaf); + } + + /* + * As long as the branch offset point is after the point where the + * search key differs, we need to branch up and find a better leaf + * node. + */ + while (to < offset) { + if (to <= searchlen && to <= foundlen && search[to] < found[to]) + { + /* + * Every leaf is greater than the one we wanted, so + * go to the parent branch and iterate back to the + * predecessor from that point. + */ + iter->sp--; + prevleaf(iter); + n = iter->stack[iter->sp]; + leaf = n; + } else { + if (is_branch(n)) { + n = greatest_leaf(qp, n, iter); + } + return (n); + } + + foundlen = leaf_qpkey(qp, leaf, found); + to = qpkey_compare(search, searchlen, found, foundlen); + } + + if (is_branch(n)) { + /* + * We're on the right branch, so find the best match. + */ + prefetch_twigs(qp, n); + dns_qpnode_t *twigs = branch_twigs(qp, n); + dns_qpweight_t pos = branch_twig_pos(n, bit); + + if (pos == 0) { + /* + * Every leaf in the branch is greater than the one we + * wanted; use the iterator to walk back to the + * predecessor. + */ + prevleaf(iter); + n = iter->stack[iter->sp--]; + } else { + /* + * The name we want would've been after some twig in + * this branch. point n to that twig, then walk down + * to the highest leaf in that subtree to get + * the predecessor. + */ + n = greatest_leaf(qp, twigs + pos - 1, iter); + } + + } else { + /* + * We're on the right leaf, either the iterator already points + * to the rightful predecessor, or it points to an immediate + * successor. If the latter, we can now use the qpiter stack + * we've constructed to step back to the predecessor. Otherwise, + * we don't have to do anything anymore. + */ + if (to <= searchlen && to <= foundlen && search[to] < found[to]) + { + prevleaf(iter); + } + } + + return (n); +} + isc_result_t dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain, @@ -2057,12 +2192,12 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, dns_qpkey_t search, found; size_t searchlen, foundlen; size_t offset = 0; - size_t difpos = 0; dns_qpnode_t *n = NULL; + dns_qpshift_t bit = SHIFT_NOBYTE; dns_qpchain_t oc; dns_qpiter_t it; - bool matched = true; - bool getpred = true; + bool matched = false; + bool setiter = true; REQUIRE(QP_VALID(qp)); REQUIRE(foundname == NULL || ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); @@ -2074,7 +2209,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, } if (iter == NULL) { iter = ⁢ - getpred = false; + setiter = false; } dns_qpchain_init(qp, chain); dns_qpiter_init(qp, iter); @@ -2096,7 +2231,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, prefetch_twigs(qp, n); offset = branch_key_offset(n); - dns_qpshift_t bit = qpkey_bit(search, searchlen, offset); + bit = qpkey_bit(search, searchlen, offset); dns_qpnode_t *twigs = branch_twigs(qp, n); /* @@ -2111,7 +2246,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * want when `off == 0`. * * Note 2: If SHIFT_NOBYTE twig is present, it will always - * be in position 0, the first localtion in 'twigs'. + * be in position 0, the first location in 'twigs'. */ if (bit != SHIFT_NOBYTE && branch_has_twig(n, SHIFT_NOBYTE) && qpkey_bit(search, searchlen, offset - 1) == SHIFT_NOBYTE && @@ -2128,144 +2263,53 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * the loop. */ n = branch_twig_ptr(qp, n, bit); - } else if (getpred) { + } else if (setiter) { /* * this branch is a dead end. however, the caller - * passed us an iterator, so we need to find the - * predecessor of the searched-for-name. - * first step: find out if we've overshot - * the search key; we do that by finding an - * arbitrary leaf to compare against. + * passed us an iterator, so we'll need to look + * for the predecessor of the searched-for-name; + * that will break the loop. */ - size_t to; - dns_qpnode_t *least = n; - - least: - while (is_branch(least)) { - least = branch_twigs(qp, least); - } - foundlen = leaf_qpkey(qp, least, found); - to = qpkey_compare(search, searchlen, found, foundlen); - if (to >= offset) { - /* - * we're on the right branch, so find - * the best match. - */ - - dns_qpweight_t pos = branch_twig_pos(n, bit); - if (pos == 0) { - /* - * every leaf in the branch is greater - * than the one we wanted; use the - * iterator to walk back to the - * predecessor. - */ - prevleaf(iter); - n = iter->stack[iter->sp--]; - } else { - /* - * the name we want would've been - * after some twig in this - * branch. point n to that twig, - * then walk down to the highest - * leaf in that subtree to get the - * predecessor. - */ - n = greatest_leaf(qp, twigs + pos - 1, - iter); - } - } else if (to <= searchlen && to <= foundlen && - search[to] < found[to]) - { - /* - * every leaf is greater than the one - * we wanted, so iterate back to the - * predecessor. - */ - iter->sp--; - prevleaf(iter); - n = iter->stack[iter->sp]; - least = n; - goto least; - } else if (is_branch(n)) { - /* - * every leaf is less than the one we - * wanted, so get the highest. - */ - n = greatest_leaf(qp, n, iter); - } + n = fix_iterator(qp, iter, n, search, searchlen, bit, + offset); } else { /* * this branch is a dead end, and the predecessor * doesn't matter. now we just need to find a leaf - * to end on so qpkey_leaf() will work below. + * to end on so that qpkey_leaf() will work below. */ if (chain->len > 0) { /* we saved an ancestor leaf: use that */ n = chain->chain[chain->len - 1].node; } else { /* walk down to find the leftmost leaf */ - while (is_branch(twigs)) { - twigs = branch_twigs(qp, twigs); - } - n = twigs; + n = anyleaf(qp, twigs); } } iter->stack[++iter->sp] = n; } - /* do the keys differ, and if so, where? */ - foundlen = leaf_qpkey(qp, n, found); - difpos = qpkey_compare(search, searchlen, found, foundlen); - - /* - * 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, then the search ended at a leaf. - * if it was not an exact match, then we're now pointing - * at either the immediate predecessor or the immediate - * successor of the searched-for name; if successor, we can - * now use the qpiter stack we've constructed to step back to - * the predecessor. if we're pointed at the predecessor - * or 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 (getpred && matched && difpos != QPKEY_EQUAL) { - while (difpos < offset) { - if (difpos <= searchlen && difpos <= foundlen && - search[difpos] < found[difpos]) - { - iter->sp--; - prevleaf(iter); - n = iter->stack[iter->sp]; - } else { - break; - } - - foundlen = leaf_qpkey(qp, n, found); - difpos = qpkey_compare(search, searchlen, found, - foundlen); - } - - if (difpos <= searchlen && difpos <= foundlen && - search[difpos] < found[difpos]) - { - prevleaf(iter); - } + if (matched && setiter) { + /* + * we found a leaf on a matching twig, but it + * might not be the leaf we wanted. if it isn't, + * and if the caller passed us an iterator, + * then we might need to reposition it. + */ + n = fix_iterator(qp, iter, n, search, searchlen, bit, offset); } - if (difpos == QPKEY_EQUAL || difpos == foundlen) { + /* do the keys differ, and if so, where? */ + foundlen = leaf_qpkey(qp, n, found); + offset = qpkey_compare(search, searchlen, found, foundlen); + + if (offset == QPKEY_EQUAL || offset == foundlen) { SET_IF_NOT_NULL(pval_r, leaf_pval(n)); SET_IF_NOT_NULL(ival_r, leaf_ival(n)); maybe_set_name(qp, n, foundname); - add_link(chain, n, difpos); - if (difpos == QPKEY_EQUAL) { + add_link(chain, n, offset); + if (offset == QPKEY_EQUAL) { return (ISC_R_SUCCESS); } else { return (DNS_R_PARTIALMATCH); @@ -2278,7 +2322,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, */ int len = chain->len; while (len-- > 0) { - if (difpos >= chain->chain[len].offset) { + if (offset >= chain->chain[len].offset) { n = chain->chain[len].node; SET_IF_NOT_NULL(pval_r, leaf_pval(n)); SET_IF_NOT_NULL(ival_r, leaf_ival(n));