/* Define to 1 for detailed reference tracing */
#undef DNS_NAMETREE_TRACE
-typedef enum { DNS_NAMETREE_BOOL, DNS_NAMETREE_BITS } dns_nametree_type_t;
+typedef enum {
+ DNS_NAMETREE_BOOL,
+ DNS_NAMETREE_BITS,
+ DNS_NAMETREE_COUNT
+} dns_nametree_type_t;
ISC_LANG_BEGINDECLS
* for debugging purposes.
*
* 'type' indicates whether the tree will be used for storing boolean
- * values (DNS_NAMETREE_BOOL) or bitfields (DNS_NAMETREE_BITS).
+ * values (DNS_NAMETREE_BOOL), bitfields (DNS_NAMETREE_BITS), or counters
+ * (DNS_NAMETREE_COUNT).
*
* Requires:
*
* represents a single boolean value, true or false. If the name already
* exists within the tree, then return ISC_R_EXISTS.
*
+ * If the nametree type was set to DNS_NAMETREE_COUNT, then 'value'
+ * can only be true. Each time the same name is added to the tree,
+ * ISC_R_SUCCESS is returned and a counter is incremented.
+ * dns_nametree_delete() must be deleted the same number of times
+ * as dns_nametree_add() before the name is removed from the tree.
+ *
* If the nametree type was set to DNS_NAMETREE_BITS, then 'value' is
* a bit number within a bit field, which is sized to accomodate at least
* 'value' bits. If the name already exists, then that bit will be set
/*%<
* Delete 'name' from 'nametree'.
*
+ * If the nametree type was set to DNS_NAMETREE_COUNT, then this must
+ * be called for each name the same number of times as dns_nametree_add()
+ * was called before the name is removed.
+ *
* Requires:
*
*\li 'nametree' points to a valid nametree.
destroy_ntnode(dns_ntnode_t *node) {
isc_refcount_destroy(&node->references);
if (node->bits != NULL) {
- isc_mem_cput(node->mctx, node->bits, 8, sizeof(uint32_t));
+ isc_mem_cput(node->mctx, node->bits, node->bits[0],
+ sizeof(char));
}
isc_mem_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t));
}
static void
destroy_nametree(dns_nametree_t *nametree) {
+ /* dns_qpread_t qpr; */
+ /* dns_qpiter_t iter; */
+ /* void *pval = NULL; */
+
nametree->magic = 0;
+ /* dns_qpmulti_query(nametree->table, &qpr); */
+ /* dns_qpiter_init(&qpr, &iter); */
+ /* while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) { */
+ /* dns_ntnode_t *n = pval; */
+ /* dns_ntnode_detach(&n); */
+ /* } */
+ /* dns_qpread_destroy(nametree->table, &qpr); */
+
dns_qpmulti_destroy(&nametree->table);
isc_refcount_destroy(&nametree->references);
static bool
matchbit(unsigned char *bits, uint32_t val) {
- unsigned int len = val / 8;
+ unsigned int len = val / 8 + 2;
unsigned int mask = 1 << (val % 8);
- if ((bits[len] & mask) != 0) {
+ if (len <= bits[0] && (bits[len - 1] & mask) != 0) {
return (true);
}
return (false);
uint32_t value) {
isc_result_t result;
dns_qp_t *qp = NULL;
- unsigned int len, mask;
+ uint32_t size, pos, mask, count = 0;
dns_ntnode_t *old = NULL, *new = NULL;
REQUIRE(VALID_NAMETREE(nametree));
new->set = value;
break;
+ case DNS_NAMETREE_COUNT:
+ new = newnode(nametree->mctx, name);
+ new->set = true;
+ result = dns_qp_deletename(qp, name, (void **)&old, &count);
+ if (result == ISC_R_SUCCESS) {
+ count += 1;
+ }
+ break;
+
case DNS_NAMETREE_BITS:
result = dns_qp_getname(qp, name, (void **)&old, NULL);
if (result == ISC_R_SUCCESS && matchbit(old->bits, value)) {
goto out;
}
- len = value / 8;
+ size = pos = value / 8 + 2;
mask = 1 << (value % 8);
+ if (old != NULL && old->bits[0] > pos) {
+ size = old->bits[0];
+ }
+
new = newnode(nametree->mctx, name);
- new->bits = isc_mem_cget(nametree->mctx, 8, sizeof(value));
+ new->bits = isc_mem_cget(nametree->mctx, size, sizeof(char));
if (result == ISC_R_SUCCESS) {
- INSIST(old != NULL);
memmove(new->bits, old->bits, old->bits[0]);
result = dns_qp_deletename(qp, name, NULL, NULL);
INSIST(result == ISC_R_SUCCESS);
}
- new->bits[len] |= mask;
+ new->bits[pos - 1] |= mask;
+ new->bits[0] = size;
break;
default:
UNREACHABLE();
}
- result = dns_qp_insert(qp, new, 0);
-
+ result = dns_qp_insert(qp, new, count);
/*
* We detach the node here, so any dns_qp_deletename() will
* destroy the node directly.
dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name) {
isc_result_t result;
dns_qp_t *qp = NULL;
- void *pval = NULL;
+ dns_ntnode_t *old = NULL;
+ uint32_t count;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
dns_qpmulti_write(nametree->table, &qp);
- result = dns_qp_deletename(qp, name, &pval, NULL);
- if (result == ISC_R_SUCCESS) {
- dns_ntnode_t *n = pval;
- dns_ntnode_detach(&n);
+ result = dns_qp_deletename(qp, name, (void **)&old, &count);
+ switch (nametree->type) {
+ case DNS_NAMETREE_BOOL:
+ case DNS_NAMETREE_BITS:
+ break;
+
+ case DNS_NAMETREE_COUNT:
+ if (result == ISC_R_SUCCESS && count-- != 0) {
+ dns_ntnode_t *new = newnode(nametree->mctx, name);
+ new->set = true;
+ result = dns_qp_insert(qp, new, count);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_ntnode_detach(&new);
+ }
+ break;
+ default:
+ UNREACHABLE();
}
dns_qp_compact(qp, DNS_QPGC_MAYBE);
dns_qpmulti_commit(nametree->table, &qp);
dns_nametree_find(dns_nametree_t *nametree, const dns_name_t *name,
dns_ntnode_t **ntnodep) {
isc_result_t result;
+ dns_ntnode_t *node = NULL;
dns_qpread_t qpr;
- void *pval = NULL;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
REQUIRE(ntnodep != NULL && *ntnodep == NULL);
dns_qpmulti_query(nametree->table, &qpr);
- result = dns_qp_getname(&qpr, name, &pval, NULL);
+ result = dns_qp_getname(&qpr, name, (void **)&node, NULL);
if (result == ISC_R_SUCCESS) {
- dns_ntnode_t *knode = pval;
- dns_ntnode_attach(knode, ntnodep);
+ dns_ntnode_attach(node, ntnodep);
}
dns_qpread_destroy(nametree->table, &qpr);
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, (void **)&node, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
- if (nametree->type == DNS_NAMETREE_BOOL) {
+ switch (nametree->type) {
+ case DNS_NAMETREE_BOOL:
ret = node->set;
- } else {
+ break;
+ case DNS_NAMETREE_COUNT:
+ ret = true;
+ break;
+ case DNS_NAMETREE_BITS:
ret = matchbit(node->bits, bit);
+ break;
}
}
dns_nametree_t *booltree = NULL;
dns_nametree_t *bitstree = NULL;
+dns_nametree_t *counttree = NULL;
/*
* Test utilities. In general, these assume input parameters are valid
dns_nametree_create(mctx, DNS_NAMETREE_BOOL, "bool test", &booltree);
dns_nametree_create(mctx, DNS_NAMETREE_BITS, "bits test", &bitstree);
+ dns_nametree_create(mctx, DNS_NAMETREE_COUNT, "count test", &counttree);
/* Add a positive boolean node */
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
- /* Add a bitfield nodes under a parent */
+ /* Add a bitfield node under a parent */
dns_test_namefromstring("sub.example.com.", &fn);
assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
}
if (bitstree != NULL) {
dns_nametree_detach(&bitstree);
}
+ if (counttree != NULL) {
+ dns_nametree_detach(&counttree);
+ }
rcu_barrier();
}
destroy_tables();
}
+ISC_RUN_TEST_IMPL(add_count) {
+ dns_fixedname_t fn;
+ dns_name_t *name = dns_fixedname_name(&fn);
+
+ create_tables();
+
+ /* add a counter node five times */
+ dns_test_namefromstring("example.com.", &fn);
+ assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
+
+ /* delete it five times, checking coverage each time */
+ assert_true(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
+
+ assert_true(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
+
+ assert_true(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
+
+ assert_true(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
+
+ assert_true(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
+
+ assert_false(dns_nametree_covered(counttree, name, 0));
+ assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_NOTFOUND);
+
+ destroy_tables();
+}
+
ISC_RUN_TEST_IMPL(covered_bool) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
ISC_TEST_LIST_START
ISC_TEST_ENTRY(add_bool)
ISC_TEST_ENTRY(add_bits)
+ISC_TEST_ENTRY(add_count)
ISC_TEST_ENTRY(covered_bool)
ISC_TEST_ENTRY(covered_bits)
ISC_TEST_ENTRY(delete)