unsigned int magic;
dns_qpreader_t *qp;
uint16_t sp;
- struct __attribute__((__packed__)) {
- uint32_t ref;
- uint8_t more;
- } stack[DNS_QP_MAXKEY];
+ void *stack[DNS_QP_MAXKEY];
} dns_qpiter_t;
/*%
*/
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()`.
*
* 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
* }
*
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);
}
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);
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);
}
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];
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];
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);
n = ref_ptr(qp, qp->root_ref);
while (is_branch(n)) {
prefetch_twigs(qp, n);
+ qp_ref_t ref = branch_twigs_ref(n);
bit = branch_keybit(n, new_key, new_keylen);
pos = branch_has_twig(n, bit) ? branch_twig_pos(n, bit) : 0;
- n = branch_twigs(qp, n) + pos;
+ n = ref_ptr(qp, ref + pos);
}
/* do the keys differ, and if so, where? */
dns_qpreader_t *qp = dns_qpreader(qpr);
REQUIRE(QP_VALID(qp));
REQUIRE(qpi != NULL);
- qpi->magic = QPITER_MAGIC;
- qpi->qp = qp;
- qpi->sp = 0;
- qpi->stack[qpi->sp].ref = qp->root_ref;
- qpi->stack[qpi->sp].more = 0;
+ *qpi = (dns_qpiter_t){
+ .qp = qp,
+ .magic = QPITER_MAGIC,
+ };
+}
+
+/*
+ * are we at the last twig in this branch (in whichever direction
+ * we're iterating)?
+ */
+static bool
+last_twig(dns_qpiter_t *qpi, bool forward) {
+ qp_weight_t pos = 0, max = 0;
+ if (qpi->sp > 0) {
+ qp_node_t *child = qpi->stack[qpi->sp];
+ qp_node_t *parent = qpi->stack[qpi->sp - 1];
+ pos = child - ref_ptr(qpi->qp, branch_twigs_ref(parent));
+ if (forward) {
+ max = branch_twigs_size(parent) - 1;
+ }
+ }
+ return (pos == max);
}
/*
+ * move a QP iterator forward or back to the next or previous leaf.
* note: this function can go wrong when the iterator refers to
* a mutable view of the trie which is altered while iterating
*/
-isc_result_t
-dns_qpiter_next(dns_qpiter_t *qpi, void **pval_r, uint32_t *ival_r) {
+static isc_result_t
+iterate(bool forward, dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
+ uint32_t *ival_r) {
+ qp_node_t *node = NULL;
+ bool initial_branch = true;
+
REQUIRE(QPITER_VALID(qpi));
- REQUIRE(QP_VALID(qpi->qp));
dns_qpreader_t *qp = qpi->qp;
- if (qpi->stack[qpi->sp].ref == INVALID_REF) {
- INSIST(qpi->sp == 0);
- qpi->magic = 0;
+ REQUIRE(QP_VALID(qp));
+
+ node = get_root(qp);
+ if (node == NULL) {
return (ISC_R_NOMORE);
}
- /* push branch nodes onto the stack until we reach a leaf */
- for (;;) {
- qp_node_t *n = ref_ptr(qp, qpi->stack[qpi->sp].ref);
- if (node_tag(n) == LEAF_TAG) {
- SET_IF_NOT_NULL(pval_r, leaf_pval(n));
- SET_IF_NOT_NULL(ival_r, leaf_ival(n));
- break;
+ do {
+ if (qpi->stack[qpi->sp] == NULL) {
+ /* newly initialized iterator: use the root node */
+ INSIST(qpi->sp == 0);
+ qpi->stack[0] = node;
+ } else if (!initial_branch) {
+ /*
+ * in a prior loop, we reached a branch; from
+ * here we just need to get the highest or lowest
+ * leaf in the subtree; we don't need to bother
+ * stepping forward or backward through twigs
+ * anymore.
+ */
+ INSIST(qpi->sp > 0);
+ } else if (last_twig(qpi, forward)) {
+ /*
+ * we've stepped to the end (or the beginning,
+ * if we're iterating backwards) of a set of twigs.
+ */
+ if (qpi->sp == 0) {
+ /*
+ * we've finished iterating. reinitialize
+ * the iterator, then return ISC_R_NOMORE.
+ */
+ dns_qpiter_init(qpi->qp, qpi);
+ return (ISC_R_NOMORE);
+ }
+
+ /*
+ * pop the stack, and resume at the parent branch.
+ */
+ qpi->stack[qpi->sp] = NULL;
+ qpi->sp--;
+ continue;
+ } else {
+ /*
+ * there are more twigs in the current branch,
+ * so step the node pointer forward (or back).
+ */
+ node = qpi->stack[qpi->sp];
+ node += (forward ? 1 : -1);
+ qpi->stack[qpi->sp] = node;
}
- qpi->sp++;
- INSIST(qpi->sp < DNS_QP_MAXKEY);
- qpi->stack[qpi->sp].ref = branch_twigs_ref(n);
- qpi->stack[qpi->sp].more = branch_twigs_size(n) - 1;
- }
- /* pop the stack until we find a twig with a successor */
- while (qpi->sp > 0 && qpi->stack[qpi->sp].more == 0) {
- qpi->sp--;
- }
+ /*
+ * if we're at a branch now, push its first (or last) twig
+ * onto the stack and loop again.
+ */
+ if (is_branch(node)) {
+ qpi->sp++;
+ INSIST(qpi->sp < DNS_QP_MAXKEY);
- /* move across to the next twig */
- if (qpi->stack[qpi->sp].more > 0) {
- qpi->stack[qpi->sp].more--;
- qpi->stack[qpi->sp].ref++;
- } else {
- INSIST(qpi->sp == 0);
- qpi->stack[qpi->sp].ref = INVALID_REF;
- }
+ qp_node_t *twigs = ref_ptr(qp, branch_twigs_ref(node));
+ if (!forward) {
+ twigs += branch_twigs_size(node) - 1;
+ }
+ node = qpi->stack[qpi->sp] = twigs;
+ initial_branch = false;
+ }
+ } while (is_branch(node));
+
+ /* we're at a leaf: return its data to the caller */
+ 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);
}
+isc_result_t
+dns_qpiter_next(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
+ uint32_t *ival_r) {
+ return (iterate(true, qpi, name, pval_r, ival_r));
+}
+
+isc_result_t
+dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
+ uint32_t *ival_r) {
+ return (iterate(false, qpi, name, pval_r, ival_r));
+}
+
/***********************************************************************
*
* search
while (is_branch(n)) {
prefetch_twigs(qp, n);
- qp_node_t *twigs = branch_twigs(qp, n);
offset = branch_key_offset(n);
qp_shift_t bit = qpkey_bit(search, searchlen, offset);
+ qp_node_t *twigs = branch_twigs(qp, n);
/*
* A shorter key that can be a parent domain always has a
* `qpkey_bit()` will return SHIFT_NOBYTE, which is what we
* want when `off == 0`.
*
- * Note 2: Any SHIFT_NOBYTE twig is always `twigs[0]`.
+ * Note 2: If SHIFT_NOBYTE twig is present, it will always
+ * be in position 0, the first localtion in 'twigs'.
*/
if (bit != SHIFT_NOBYTE && branch_has_twig(n, SHIFT_NOBYTE) &&
qpkey_bit(search, searchlen, offset - 1) == SHIFT_NOBYTE &&
- !is_branch(&twigs[0]))
+ !is_branch(twigs))
{
add_link(chain, twigs, offset);
}
/*
* the twig we're looking for isn't here,
* and we haven't found an ancestor yet either.
- * continue the search with whatever this branch's
+ * but we need to end the loop at a leaf, so
+ * continue down from whatever this branch's
* first twig is.
*/
- n = &twigs[0];
+ n = twigs;
} else {
/*
* this branch is a dead end, but we do have
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;
dns_name_t *name = NULL;
size_t i = 0, n = 0;
char buf[BUFSIZ];
- void *pval = NULL;
- uint32_t ival;
if (argc != 2) {
usage();
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();
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);
/* 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;
}
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);
}
return (0);
}
size_t max_height = 0;
- qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs(qp, n);
+ qp_weight_t size = branch_twigs_size(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 (leaf_qpkey(qp, n, key));
}
size_t max_len = 0;
- qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs(qp, n);
+ qp_weight_t size = branch_twigs_size(n);
for (qp_weight_t pos = 0; pos < size; pos++) {
size_t len = maxkeylen(qp, &twigs[pos]);
max_len = ISC_MAX(max_len, len);
--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;
}
}
}
printf("}}\"];\n");
- qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs(qp, n);
+ qp_weight_t size = branch_twigs_size(n);
for (qp_weight_t pos = 0; pos < size; pos++) {
dumpdot_name(n);