}
struct addrtree* addrtree_create(addrlen_t max_depth,
- void (*delfunc)(void *, void *), size_t (*sizefunc)(void *), void *env)
+ void (*delfunc)(void *, void *), size_t (*sizefunc)(void *),
+ void *env, unsigned int max_node_count)
{
struct addrtree* tree;
log_assert(delfunc != NULL);
tree->sizefunc = sizefunc;
tree->env = env;
tree->node_count = 0;
+ tree->max_node_count = max_node_count;
return tree;
}
-/** Add node to LRU list as most recently used. */
-void lru_push(struct addrtree *tree, struct addrnode *node)
-{
- if (!tree->first) {
- tree->first = node;
- tree->last = node;
- node->prev = NULL;
- } else {
- tree->last->next = node;
- node->prev = tree->last->next;
- tree->last = node;
- }
- node->next = NULL;
-}
-
-/** Remove least recently used from LRU list */
-struct addrnode* lru_popfirst(struct addrtree *tree)
+/**
+ * Scrub a node clean of elem
+ * @param tree: Tree the node lives in.
+ * @param node: Node to be cleaned
+ */
+static void
+clean_node(struct addrtree* tree, struct addrnode* node)
{
- struct addrnode* pop;
- pop = tree->first;
- log_assert(first != NULL);
- if (!pop->next) {
- tree->first = NULL;
- tree->last = NULL;
- } else {
- tree->first = pop->next;
- tree->first->prev = NULL;
- }
- return pop;
+ if (!node->elem) return;
+ tree->delfunc(tree->env, node->elem);
+ node->elem = NULL;
}
/** Remove specified node from LRU list */
void lru_pop(struct addrtree *tree, struct addrnode *node)
{
if (node == tree->first) {
- (void)lru_popfirst(tree);
- } else if (node == tree->last) {
+ if (!node->next) { /* it is the last as well */
+ tree->first = NULL;
+ tree->last = NULL;
+ } else {
+ tree->first = node->next;
+ tree->first->prev = NULL;
+ }
+ } else if (node == tree->last) { /* but not the first */
tree->last = node->prev;
tree->last->next = NULL;
} else {
}
}
-/** Move node to the end of LRU list */
-void lru_update(struct addrtree *tree, struct addrnode *node)
-{
- lru_pop(tree, node);
- lru_push(tree, node);
-}
-
-/** Size in bytes of this data structure */
-size_t addrtree_size(const struct addrtree* tree)
+/** Add node to LRU list as most recently used. */
+void lru_push(struct addrtree *tree, struct addrnode *node)
{
- struct addrnode* n;
- size_t s;
-
- if (!tree) return 0;
- s = sizeof (struct addrnode); /* root always exists but not in LRU */
- if (tree->root->elem) s += tree->sizefunc(tree->root->elem);
- for(n = tree->first; n; n = n->next) {
- s += sizeof (struct addredge) + sizeof (struct addrnode);
- s += tree->sizefunc(n->elem);
+ if (!tree->first) {
+ tree->first = node;
+ tree->last = node;
+ node->prev = NULL;
+ } else {
+ tree->last->next = node;
+ node->prev = tree->last;
+ tree->last = node;
}
- return s;
+ node->next = NULL;
}
-/**
- * Scrub a node clean of elem
- * @param tree: Tree the node lives in.
- * @param node: Node to be cleaned
- */
-static void
-clean_node(struct addrtree* tree, struct addrnode* node)
+/** Move node to the end of LRU list */
+void lru_update(struct addrtree *tree, struct addrnode *node)
{
- tree->node_count--;
- if (!node->elem) return;
- tree->delfunc(tree->env, node->elem);
- node->elem = NULL;
+ lru_pop(tree, node);
+ lru_push(tree, node);
}
/**
* @param tree: Tree the node lives in.
* @param node: Node to be freed
*/
-static void
+static int
purge_node(struct addrtree *tree, struct addrnode *node)
{
struct addredge *parent_edge, *child_edge = NULL;
clean_node(tree, node);
parent_edge = node->parent_edge;
if (keep || !parent_edge) return;
+ tree->node_count--;
index = parent_edge->parent_index;
child_edge = node->edge[!node->edge[0]];
if (child_edge) {
parent_edge->parent_node->edge[index] = child_edge;
free(parent_edge->str);
free(parent_edge);
+ lru_pop(tree, node);
free(node);
}
+/** If a limit is set remove old nodes while above that limit */
+void lru_cleanup(struct addrtree *tree)
+{
+ struct addrnode *n, *p;
+ int children;
+ if (tree->max_node_count == 0) return;
+ while (tree->node_count > tree->max_node_count) {
+ n = tree->first;
+ if (!n) break;
+ children = (n->edge[0] != NULL) + (n->edge[1] != NULL);
+ /** Don't remove this node, it is either the root or we can't
+ * do without it because it has 2 children */
+ if (children == 2 || !n->parent_edge) {
+ lru_update(tree, n);
+ continue;
+ }
+ p = n->parent_edge->parent_node;
+ purge_node(tree, n);
+ /** Since we removed n, n's parent p is eligible for deletion
+ * if it is not the root node, caries no data and has only 1
+ * child */
+ children = (p->edge[0] != NULL) + (p->edge[1] != NULL);
+ if (!p->elem && children == 1 && p->parent_edge) {
+ purge_node(tree, p);
+ }
+ }
+}
+
+/** Size in bytes of this data structure */
+size_t addrtree_size(const struct addrtree* tree)
+{
+ struct addrnode* n;
+ size_t s;
+
+ if (!tree) return 0;
+ s = sizeof (struct addrnode); /* root always exists but not in LRU */
+ if (tree->root->elem) s += tree->sizefunc(tree->root->elem);
+ for(n = tree->first; n; n = n->next) {
+ s += sizeof (struct addredge) + sizeof (struct addrnode);
+ s += tree->sizefunc(n->elem);
+ }
+ return s;
+}
+
/**
* Free complete addrtree structure
* @param tree: Tree to free
if (!tree) return;
clean_node(tree, tree->root);
free(tree->root);
- while(n = tree->first) {
+ while((n = tree->first)) {
tree->first = n->next;
clean_node(tree, n);
free(n->parent_edge->str);
if (!edge->node->elem || edge->node->ttl >= now)
break;
purge_node(tree, edge->node);
- lru_pop(tree, edge->node);
edge = node->edge[index];
}
/* Case 2: New leafnode */
if (!newnode) return;
if (!edge_create(newnode, addr, sourcemask, node, index)) {
clean_node(tree, newnode);
+ tree->node_count--;
free(newnode);
return;
}
lru_push(tree, newnode);
+ lru_cleanup(tree);
return;
}
/* Case 3: Traverse edge */
return;
if (!edge_create(newnode, addr, common, node, index)) {
clean_node(tree, newnode);
+ tree->node_count--;
free(newnode);
return;
}
lru_push(tree, newnode);
- /** connect existing child to our new node */
+ /* connect existing child to our new node */
index = getbit(edge->str, edge->len, common);
newnode->edge[index] = edge;
edge->parent_node = newnode;
newnode = node_create(tree, elem, scope, ttl);
if (!edge_create(newnode, addr, sourcemask, node, index^1)) {
clean_node(tree, newnode);
+ tree->node_count--;
free(newnode);
return;
}
lru_push(tree, newnode);
}
+ lru_cleanup(tree);
return;
}
}
{
struct addredge* edge;
int i, s, byte;
- if (indent == 0) printf("-----Tree-----");
+ if (indent == 0) printf("-----Tree-----\n");
if (indent > maxdepth) {
printf("\n");
return;
}
- printf("[node elem:%d]\n", node->elem != NULL);
+ printf("[node elem:%d] (%d)\n", node->elem != NULL, node);
for (i = 0; i<2; i++) {
if (node->edge[i]) {
for (s = 0; s < indent; s++) printf(" ");
unit_show_func("edns-subnet/addrtree.h", "Tree consistency check");
srand(9195); /* just some value for reproducibility */
- t = addrtree_create(100, &elemfree, NULL, &env);
+ t = addrtree_create(100, &elemfree, NULL, &env, 0);
count = t->node_count;
unit_assert(count == 0);
for (i = 0; i < 1000; i++) {
}
addrtree_delete(t);
unit_show_func("edns-subnet/addrtree.h", "Tree consistency with purge");
- t = addrtree_create(8, &elemfree, NULL, &env);
+ t = addrtree_create(8, &elemfree, NULL, &env, 0);
+ unit_assert(t->node_count == 0);
+ for (i = 0; i < 1000; i++) {
+ l = randomkey(&k, 128);
+ elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
+ addrtree_insert(t, k, l, 64, elem, i + 10, i);
+ free(k);
+ unit_assert( !addrtree_inconsistent(t) );
+ }
+ addrtree_delete(t);
+ unit_show_func("edns-subnet/addrtree.h", "Tree consistency with limit");
+ t = addrtree_create(8, &elemfree, NULL, &env, 27);
unit_assert(t->node_count == 0);
for (i = 0; i < 1000; i++) {
l = randomkey(&k, 128);
elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
addrtree_insert(t, k, l, 64, elem, i + 10, i);
+ unit_assert( t->node_count <= 27);
free(k);
unit_assert( !addrtree_inconsistent(t) );
}