[BACK]Return to radtree.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / nsd

File: [local] / src / usr.sbin / nsd / radtree.c (download)

Revision 1.6, Sun Oct 24 12:14:19 2021 UTC (2 years, 7 months ago) by florian
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, HEAD
Changes since 1.5: +1 -1 lines

Update to 4.3.8.
OK sthen

/*
 * radtree -- generic radix tree for binary strings.
 *
 * Copyright (c) 2010, NLnet Labs.  See LICENSE for license.
 */
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "radtree.h"
#include "util.h"
#include "region-allocator.h"

#include <stdio.h>
#include <ctype.h>

struct radtree* radix_tree_create(struct region* region)
{
	struct radtree* rt = (struct radtree*)region_alloc(region, sizeof(*rt));
	if(!rt) return NULL;
	rt->region = region;
	radix_tree_init(rt);
	return rt;
}

void radix_tree_init(struct radtree* rt)
{
	rt->root = NULL;
	rt->count = 0;
}

/** delete radnodes in postorder recursion */
static void radnode_del_postorder(struct region* region, struct radnode* n)
{
	unsigned i;
	if(!n) return;
	for(i=0; i<n->len; i++) {
		radnode_del_postorder(region, n->array[i].node);
		region_recycle(region, n->array[i].str, n->array[i].len);
	}
	region_recycle(region, n->array, n->capacity*sizeof(struct radsel));
	region_recycle(region, n, sizeof(*n));
}

void radix_tree_clear(struct radtree* rt)
{
	radnode_del_postorder(rt->region, rt->root);
	rt->root = NULL;
	rt->count = 0;
}

void radix_tree_delete(struct radtree* rt)
{
	if(!rt) return;
	radix_tree_clear(rt);
	region_recycle(rt->region, rt, sizeof(*rt));
}

/** return last elem-containing node in this subtree (excl self) */
static struct radnode*
radnode_last_in_subtree(struct radnode* n)
{
	int idx;
	/* try last entry in array first */
	for(idx=((int)n->len)-1; idx >= 0; idx--) {
		if(n->array[idx].node) {
			/* does it have entries in its subtrees? */
			if(n->array[idx].node->len > 0) {
				struct radnode* s = radnode_last_in_subtree(
					n->array[idx].node);
				if(s) return s;
			}
			/* no, does it have an entry itself? */
			if(n->array[idx].node->elem)
				return n->array[idx].node;
		}
	}
	return NULL;
}

/** last in subtree, incl self */
static struct radnode*
radnode_last_in_subtree_incl_self(struct radnode* n)
{
	struct radnode* s = radnode_last_in_subtree(n);
	if(s) return s;
	if(n->elem) return n;
	return NULL;
}

/** return first elem-containing node in this subtree (excl self) */
static struct radnode*
radnode_first_in_subtree(struct radnode* n)
{
	unsigned idx;
	struct radnode* s;
	/* try every subnode */
	for(idx=0; idx<n->len; idx++) {
		if(n->array[idx].node) {
			/* does it have elem itself? */
			if(n->array[idx].node->elem)
				return n->array[idx].node;
			/* try its subtrees */
			if((s=radnode_first_in_subtree(n->array[idx].node))!=0)
				return s;
		}
	}
	return NULL;
}

/** Find an entry in arrays from idx-1 to 0 */
static struct radnode*
radnode_find_prev_from_idx(struct radnode* n, unsigned from)
{
	unsigned idx = from;
	while(idx > 0) {
		idx --;
		if(n->array[idx].node) {
			struct radnode* s = radnode_last_in_subtree_incl_self(
				n->array[idx].node);
			if(s) return s;
		}
	}
	return NULL;
}

/** 
 * Find a prefix of the key, in whole-nodes.
 * Finds the longest prefix that corresponds to a whole radnode entry.
 * There may be a slightly longer prefix in one of the array elements.
 * @param result: the longest prefix, the entry itself if *respos==len,
 * 	otherwise an array entry, residx.
 * @param respos: pos in string where next unmatched byte is, if == len an
 * 	exact match has been found.  If == 0 then a "" match was found.
 * @return false if no prefix found, not even the root "" prefix.
 */
static int radix_find_prefix_node(struct radtree* rt, uint8_t* k,
	radstrlen_type len, struct radnode** result, radstrlen_type* respos)
{
	struct radnode* n = rt->root;
	radstrlen_type pos = 0;
	uint8_t byte;
	*respos = 0;
	*result = n;
	if(!n) return 0;
	while(n) {
		if(pos == len) {
			return 1;
		}
		byte = k[pos];
		if(byte < n->offset) {
			return 1;
		}
		byte -= n->offset;
		if(byte >= n->len) {
			return 1;
		}
		pos++;
		if(n->array[byte].len != 0) {
			/* must match additional string */
			if(pos+n->array[byte].len > len) {
				return 1;
			}
			if(memcmp(&k[pos], n->array[byte].str,
				n->array[byte].len) != 0) {
				return 1;
			}
			pos += n->array[byte].len;
		}
		n = n->array[byte].node;
		if(!n) return 1;
		*respos = pos;
		*result = n;
	}
	/* cannot reach because of returns when !n above */
	/* ENOTREACH */
	return 1;
}

/** grow array to at least the given size, offset unchanged */
static int
radnode_array_grow(struct region* region, struct radnode* n, unsigned want)
{
	unsigned ns = ((unsigned)n->capacity)*2;
	struct radsel* a;
	assert(want <= 256); /* cannot be more, range of uint8 */
	if(want > ns)
		ns = want;
	if(ns > 256) ns = 256;
	/* we do not use realloc, because we want to keep the old array
	 * in case alloc fails, so that the tree is still usable */
	a = (struct radsel*)region_alloc_array(region, ns, sizeof(struct radsel));
	if(!a) return 0;
	assert(n->len <= n->capacity);
	assert(n->capacity < ns);
	memcpy(&a[0], &n->array[0], n->len*sizeof(struct radsel));
	region_recycle(region, n->array, n->capacity*sizeof(struct radsel));
	n->array = a;
	n->capacity = ns;
	return 1;
}

/** make space in radnode array for another byte */
static int
radnode_array_space(struct region* region, struct radnode* n, uint8_t byte)
{
	/* is there an array? */
	if(!n->array || n->capacity == 0) {
		n->array = (struct radsel*)region_alloc(region,
			sizeof(struct radsel));
		if(!n->array) return 0;
		memset(&n->array[0], 0, sizeof(struct radsel));
		n->len = 1;
		n->capacity = 1;
		n->offset = byte;
	/* is the array unused? */
	} else if(n->len == 0 && n->capacity != 0) {
		n->len = 1;
		n->offset = byte;
		memset(&n->array[0], 0, sizeof(struct radsel));
	/* is it below the offset? */
	} else if(byte < n->offset) {
		/* is capacity enough? */
		unsigned idx;
		unsigned need = n->offset-byte;
		if(n->len+need > n->capacity) {
			/* grow array */
			if(!radnode_array_grow(region, n, n->len+need))
				return 0;
		}
		/* reshuffle items to end */
		memmove(&n->array[need], &n->array[0],
				n->len*sizeof(struct radsel));
		/* fixup pidx */
		for(idx = 0; idx < n->len; idx++) {
			if(n->array[idx+need].node)
				n->array[idx+need].node->pidx = idx+need;
		}
		/* zero the first */
		memset(&n->array[0], 0, need*sizeof(struct radsel));
		n->len += need;
		n->offset = byte;
	/* is it above the max? */
	} else if(byte-n->offset >= n->len) {
		/* is capacity enough? */
		unsigned need = (byte-n->offset) - n->len + 1;
		/* grow array */
		if(n->len + need > n->capacity) {
			if(!radnode_array_grow(region, n, n->len+need))
				return 0;
		}
		/* zero added entries */
		memset(&n->array[n->len], 0, need*sizeof(struct radsel));
		/* grow length */
		n->len += need;
	}
	return 1;
}

/** create a prefix in the array strs */
static int
radsel_str_create(struct region* region, struct radsel* r, uint8_t* k,
	radstrlen_type pos, radstrlen_type len)
{
	r->str = (uint8_t*)region_alloc(region, sizeof(uint8_t)*(len-pos));
	if(!r->str)
		return 0; /* out of memory */
	memmove(r->str, k+pos, len-pos);
	r->len = len-pos;
	return 1;
}

/** see if one byte string p is a prefix of another x (equality is true) */
static int
bstr_is_prefix(uint8_t* p, radstrlen_type plen, uint8_t* x,
	radstrlen_type xlen)
{
	/* if plen is zero, it is an (empty) prefix */
	if(plen == 0)
		return 1;
	/* if so, p must be shorter */
	if(plen > xlen)
		return 0;
	return (memcmp(p, x, plen) == 0);
}

/** number of bytes in common for the two strings */
static radstrlen_type
bstr_common(uint8_t* x, radstrlen_type xlen, uint8_t* y, radstrlen_type ylen)
{
	unsigned i, max = ((xlen<ylen)?xlen:ylen);
	for(i=0; i<max; i++) {
		if(x[i] != y[i])
			return i;
	}
	return max;
}


int
bstr_is_prefix_ext(uint8_t* p, radstrlen_type plen, uint8_t* x,
	radstrlen_type xlen)
{
	return bstr_is_prefix(p, plen, x, xlen);
}

radstrlen_type
bstr_common_ext(uint8_t* x, radstrlen_type xlen, uint8_t* y,
	radstrlen_type ylen)
{
	return bstr_common(x, xlen, y, ylen);
}

/** allocate remainder from prefixes for a split:
 * plen: len prefix, l: longer bstring, llen: length of l. */
static int
radsel_prefix_remainder(struct region* region, radstrlen_type plen,
	uint8_t* l, radstrlen_type llen,
	uint8_t** s, radstrlen_type* slen)
{
	*slen = llen - plen;
	*s = (uint8_t*)region_alloc(region, (*slen)*sizeof(uint8_t));
	if(!*s)
		return 0;
	memmove(*s, l+plen, llen-plen);
	return 1;
}

/** radsel create a split when two nodes have shared prefix.
 * @param r: radsel that gets changed, it contains a node.
 * @param k: key byte string
 * @param pos: position where the string enters the radsel (e.g. r.str)
 * @param len: length of k.
 * @param add: additional node for the string k.
 * 	removed by called on failure.
 * @return false on alloc failure, no changes made.
 */
static int
radsel_split(struct region* region, struct radsel* r, uint8_t* k,
	radstrlen_type pos, radstrlen_type len, struct radnode* add)
{
	uint8_t* addstr = k+pos;
	radstrlen_type addlen = len-pos;
	if(bstr_is_prefix(addstr, addlen, r->str, r->len)) {
		uint8_t* split_str=NULL, *dupstr=NULL;
		radstrlen_type split_len=0;
		/* 'add' is a prefix of r.node */
		/* also for empty addstr */
		/* set it up so that the 'add' node has r.node as child */
		/* so, r.node gets moved below the 'add' node, but we do
		 * this so that the r.node stays the same pointer for its
		 * key name */
		assert(addlen != r->len);
		assert(addlen < r->len);
		if(r->len-addlen > 1) {
			/* shift one because a char is in the lookup array */
			if(!radsel_prefix_remainder(region, addlen+1, r->str,
				r->len, &split_str, &split_len))
				return 0;
		}
		if(addlen != 0) {
			dupstr = (uint8_t*)region_alloc(region,
				addlen*sizeof(uint8_t));
			if(!dupstr) {
				region_recycle(region, split_str, split_len);
				return 0;
			}
			memcpy(dupstr, addstr, addlen);
		}
		if(!radnode_array_space(region, add, r->str[addlen])) {
			region_recycle(region, split_str, split_len);
			region_recycle(region, dupstr, addlen);
			return 0;
		}
		/* alloc succeeded, now link it in */
		add->parent = r->node->parent;
		add->pidx = r->node->pidx;
		add->array[0].node = r->node;
		add->array[0].str = split_str;
		add->array[0].len = split_len;
		r->node->parent = add;
		r->node->pidx = 0;

		r->node = add;
		region_recycle(region, r->str, r->len);
		r->str = dupstr;
		r->len = addlen;
	} else if(bstr_is_prefix(r->str, r->len, addstr, addlen)) {
		uint8_t* split_str = NULL;
		radstrlen_type split_len = 0;
		/* r.node is a prefix of 'add' */
		/* set it up so that the 'r.node' has 'add' as child */
		/* and basically, r.node is already completely fine,
		 * we only need to create a node as its child */
		assert(addlen != r->len);
		assert(r->len < addlen);
		if(addlen-r->len > 1) {
			/* shift one because a character goes into array */
			if(!radsel_prefix_remainder(region, r->len+1, addstr,
				addlen, &split_str, &split_len))
				return 0;
		}
		if(!radnode_array_space(region, r->node, addstr[r->len])) {
			region_recycle(region, split_str, split_len);
			return 0;
		}
		/* alloc succeeded, now link it in */
		add->parent = r->node;
		add->pidx = addstr[r->len] - r->node->offset;
		r->node->array[add->pidx].node = add;
		r->node->array[add->pidx].str = split_str;
		r->node->array[add->pidx].len = split_len;
	} else {
		/* okay we need to create a new node that chooses between 
		 * the nodes 'add' and r.node
		 * We do this so that r.node stays the same pointer for its
		 * key name. */
		struct radnode* com;
		uint8_t* common_str=NULL, *s1_str=NULL, *s2_str=NULL;
		radstrlen_type common_len, s1_len=0, s2_len=0;
		common_len = bstr_common(r->str, r->len, addstr, addlen);
		assert(common_len < r->len);
		assert(common_len < addlen);

		/* create the new node for choice */
		com = (struct radnode*)region_alloc_zero(region, sizeof(*com));
		if(!com) return 0; /* out of memory */

		/* create the two substrings for subchoices */
		if(r->len-common_len > 1) {
			/* shift by one char because it goes in lookup array */
			if(!radsel_prefix_remainder(region, common_len+1,
				r->str, r->len, &s1_str, &s1_len)) {
				region_recycle(region, com, sizeof(*com));
				return 0;
			}
		}
		if(addlen-common_len > 1) {
			if(!radsel_prefix_remainder(region, common_len+1,
				addstr, addlen, &s2_str, &s2_len)) {
				region_recycle(region, com, sizeof(*com));
				region_recycle(region, s1_str, s1_len);
				return 0;
			}
		}

		/* create the shared prefix to go in r */
		if(common_len > 0) {
			common_str = (uint8_t*)region_alloc(region,
				common_len*sizeof(uint8_t));
			if(!common_str) {
				region_recycle(region, com, sizeof(*com));
				region_recycle(region, s1_str, s1_len);
				region_recycle(region, s2_str, s2_len);
				return 0;
			}
			memcpy(common_str, addstr, common_len);
		}

		/* make space in the common node array */
		if(!radnode_array_space(region, com, r->str[common_len]) ||
			!radnode_array_space(region, com, addstr[common_len])) {
			region_recycle(region, com->array, com->capacity*sizeof(struct radsel));
			region_recycle(region, com, sizeof(*com));
			region_recycle(region, common_str, common_len);
			region_recycle(region, s1_str, s1_len);
			region_recycle(region, s2_str, s2_len);
			return 0;
		}

		/* allocs succeeded, proceed to link it all up */
		com->parent = r->node->parent;
		com->pidx = r->node->pidx;
		r->node->parent = com;
		r->node->pidx = r->str[common_len]-com->offset;
		add->parent = com;
		add->pidx = addstr[common_len]-com->offset;
		com->array[r->node->pidx].node = r->node;
		com->array[r->node->pidx].str = s1_str;
		com->array[r->node->pidx].len = s1_len;
		com->array[add->pidx].node = add;
		com->array[add->pidx].str = s2_str;
		com->array[add->pidx].len = s2_len;
		region_recycle(region, r->str, r->len);
		r->str = common_str;
		r->len = common_len;
		r->node = com;
	}
	return 1;
}

struct radnode* radix_insert(struct radtree* rt, uint8_t* k,
	radstrlen_type len, void* elem)
{
	struct radnode* n;
	radstrlen_type pos = 0;
	/* create new element to add */
	struct radnode* add = (struct radnode*)region_alloc_zero(rt->region,
		sizeof(*add));
	if(!add) return NULL; /* out of memory */
	add->elem = elem;

	/* find out where to add it */
	if(!radix_find_prefix_node(rt, k, len, &n, &pos)) {
		/* new root */
		assert(rt->root == NULL);
		if(len == 0) {
			rt->root = add;
		} else {
			/* add a root to point to new node */
			n = (struct radnode*)region_alloc_zero(rt->region,
				sizeof(*n));
			if(!n) {
				region_recycle(rt->region, add, sizeof(*add));
				return NULL;
			}
			if(!radnode_array_space(rt->region, n, k[0])) {
				region_recycle(rt->region, n->array,
					n->capacity*sizeof(struct radsel));
				region_recycle(rt->region, n, sizeof(*n));
				region_recycle(rt->region, add, sizeof(*add));
				return NULL;
			}
			add->parent = n;
			add->pidx = 0;
			n->array[0].node = add;
			if(len > 1) {
				if(!radsel_prefix_remainder(rt->region, 1, k, len,
					&n->array[0].str, &n->array[0].len)) {
					region_recycle(rt->region, n->array,
						n->capacity*sizeof(struct radsel));
					region_recycle(rt->region, n, sizeof(*n));
					region_recycle(rt->region, add, sizeof(*add));
					return NULL;
				}
			}
			rt->root = n;
		}
	} else if(pos == len) {
		/* found an exact match */
		if(n->elem) {
			/* already exists, failure */
			region_recycle(rt->region, add, sizeof(*add));
			return NULL;
		}
		n->elem = elem;
		region_recycle(rt->region, add, sizeof(*add));
		add = n;
	} else {
		/* n is a node which can accomodate */
		uint8_t byte;
		assert(pos < len);
		byte = k[pos];

		/* see if it falls outside of array */
		if(byte < n->offset || byte-n->offset >= n->len) {
			/* make space in the array for it; adjusts offset */
			if(!radnode_array_space(rt->region, n, byte)) {
				region_recycle(rt->region, add, sizeof(*add));
				return NULL;
			}
			assert(byte>=n->offset && byte-n->offset<n->len);
			byte -= n->offset;
			/* see if more prefix needs to be split off */
			if(pos+1 < len) {
				if(!radsel_str_create(rt->region, &n->array[byte],
					k, pos+1, len)) {
					region_recycle(rt->region, add, sizeof(*add));
					return NULL;
				}
			}
			/* insert the new node in the new bucket */
			add->parent = n;
			add->pidx = byte;
			n->array[byte].node = add;
		/* so a bucket exists and byte falls in it */
		} else if(n->array[byte-n->offset].node == NULL) {
			/* use existing bucket */
			byte -= n->offset;
			if(pos+1 < len) {
				/* split off more prefix */
				if(!radsel_str_create(rt->region, &n->array[byte],
					k, pos+1, len)) {
					region_recycle(rt->region, add, sizeof(*add));
					return NULL;
				}
			}
			/* insert the new node in the new bucket */
			add->parent = n;
			add->pidx = byte;
			n->array[byte].node = add;
		} else {
			/* use bucket but it has a shared prefix,
			 * split that out and create a new intermediate
			 * node to split out between the two.
			 * One of the two might exactmatch the new 
			 * intermediate node */
			if(!radsel_split(rt->region, &n->array[byte-n->offset],
				k, pos+1, len, add)) {
				region_recycle(rt->region, add, sizeof(*add));
				return NULL;
			}
		}
	}

	rt->count ++;
	return add;
}

/** Delete a radnode */
static void radnode_delete(struct region* region, struct radnode* n)
{
	unsigned i;
	if(!n) return;
	for(i=0; i<n->len; i++) {
		/* safe to free NULL str */
		region_recycle(region, n->array[i].str, n->array[i].len);
	}
	region_recycle(region, n->array, n->capacity*sizeof(struct radsel));
	region_recycle(region, n, sizeof(*n));
}

/** Cleanup node with one child, it is removed and joined into parent[x] str */
static int
radnode_cleanup_onechild(struct region* region, struct radnode* n,
	struct radnode* par)
{
	uint8_t* join;
	radstrlen_type joinlen;
	uint8_t pidx = n->pidx;
	struct radnode* child = n->array[0].node;
	/* node had one child, merge them into the parent. */
	/* keep the child node, so its pointers stay valid. */

	/* at parent, append child->str to array str */
	assert(pidx < par->len);
	joinlen = par->array[pidx].len + n->array[0].len + 1;
	join = (uint8_t*)region_alloc(region, joinlen*sizeof(uint8_t));
	if(!join) {
		/* cleanup failed due to out of memory */
		/* the tree is inefficient, with node n still existing */
		return 0;
	}
	/* we know that .str and join are malloced, thus aligned */
	if(par->array[pidx].str)
	    memcpy(join, par->array[pidx].str, par->array[pidx].len);
	/* the array lookup is gone, put its character in the lookup string*/
	join[par->array[pidx].len] = child->pidx + n->offset;
	/* but join+len may not be aligned */
	if(n->array[0].str)
	    memmove(join+par->array[pidx].len+1, n->array[0].str, n->array[0].len);
	region_recycle(region, par->array[pidx].str, par->array[pidx].len);
	par->array[pidx].str = join;
	par->array[pidx].len = joinlen;
	/* and set the node to our child. */
	par->array[pidx].node = child;
	child->parent = par;
	child->pidx = pidx;
	/* we are unlinked, delete our node */
	radnode_delete(region, n);
	return 1;
}

/** remove array of nodes */
static void
radnode_array_clean_all(struct region* region, struct radnode* n)
{
	n->offset = 0;
	n->len = 0;
	/* shrink capacity */
	region_recycle(region, n->array, n->capacity*sizeof(struct radsel));
	n->array = NULL;
	n->capacity = 0;
}

/** see if capacity can be reduced for the given node array */
static void
radnode_array_reduce_if_needed(struct region* region, struct radnode* n)
{
	if(n->len <= n->capacity/2 && n->len != n->capacity) {
		struct radsel* a = (struct radsel*)region_alloc_array(region,
			sizeof(*a), n->len);
		if(!a) return;
		memcpy(a, n->array, sizeof(*a)*n->len);
		region_recycle(region, n->array, n->capacity*sizeof(*a));
		n->array = a;
		n->capacity = n->len;
	}
}

/** remove NULL nodes from front of array */
static void
radnode_array_clean_front(struct region* region, struct radnode* n)
{
	/* move them up and adjust offset */
	unsigned idx, shuf = 0;
	/* remove until a nonNULL entry */
	while(shuf < n->len && n->array[shuf].node == NULL)
		shuf++;
	if(shuf == 0)
		return;
	if(shuf == n->len) {
		/* the array is empty, the tree is inefficient */
		radnode_array_clean_all(region, n);
		return;
	}
	assert(shuf < n->len);
	assert((int)shuf <= 255-(int)n->offset);
	memmove(&n->array[0], &n->array[shuf],
		(n->len - shuf)*sizeof(struct radsel));
	n->offset += shuf;
	n->len -= shuf;
	for(idx=0; idx<n->len; idx++)
		if(n->array[idx].node)
			n->array[idx].node->pidx = idx;
	/* see if capacity can be reduced */
	radnode_array_reduce_if_needed(region, n);
}

/** remove NULL nodes from end of array */
static void
radnode_array_clean_end(struct region* region, struct radnode* n)
{
	/* shorten it */
	unsigned shuf = 0;
	/* remove until a nonNULL entry */
	while(shuf < n->len && n->array[n->len-1-shuf].node == NULL)
		shuf++;
	if(shuf == 0)
		return;
	if(shuf == n->len) {
		/* the array is empty, the tree is inefficient */
		radnode_array_clean_all(region, n);
		return;
	}
	assert(shuf < n->len);
	n->len -= shuf;
	/* array elements can stay where they are */
	/* see if capacity can be reduced */
	radnode_array_reduce_if_needed(region, n);
}

/** clean up radnode leaf, where we know it has a parent */
static void
radnode_cleanup_leaf(struct region* region, struct radnode* n,
	struct radnode* par)
{
	uint8_t pidx;
	/* node was a leaf */
	/* delete leaf node, but store parent+idx */
	pidx = n->pidx;
	radnode_delete(region, n);

	/* set parent+idx entry to NULL str and node.*/
	assert(pidx < par->len);
	region_recycle(region, par->array[pidx].str, par->array[pidx].len);
	par->array[pidx].str = NULL;
	par->array[pidx].len = 0;
	par->array[pidx].node = NULL;

	/* see if par offset or len must be adjusted */
	if(par->len == 1) {
		/* removed final element from array */
		radnode_array_clean_all(region, par);
	} else if(pidx == 0) {
		/* removed first element from array */
		radnode_array_clean_front(region, par);
	} else if(pidx == par->len-1) {
		/* removed last element from array */
		radnode_array_clean_end(region, par);
	}
}

/** 
 * Cleanup a radix node that was made smaller, see if it can 
 * be merged with others.
 * @param rt: tree to remove root if needed.
 * @param n: node to cleanup
 * @return false on alloc failure.
 */
static int
radnode_cleanup(struct radtree* rt, struct radnode* n)
{
	while(n) {
		if(n->elem) {
			/* cannot delete node with a data element */
			return 1;
		} else if(n->len == 1 && n->parent) {
			return radnode_cleanup_onechild(rt->region, n, n->parent);
		} else if(n->len == 0) {
			struct radnode* par = n->parent;
			if(!par) {
				/* root deleted */
				radnode_delete(rt->region, n);
				rt->root = NULL;
				return 1;
			}
			/* remove and delete the leaf node */
			radnode_cleanup_leaf(rt->region, n, par);
			/* see if parent can now be cleaned up */
			n = par;
		} else {
			/* node cannot be cleaned up */
			return 1;
		}
	}
	/* ENOTREACH */
	return 1;
}

void radix_delete(struct radtree* rt, struct radnode* n)
{
	if(!n) return;
	n->elem = NULL;
	rt->count --;
	if(!radnode_cleanup(rt, n)) {
		/* out of memory in cleanup.  the elem ptr is NULL, but
		 * the radix tree could be inefficient. */
	}
}

struct radnode* radix_search(struct radtree* rt, uint8_t* k,
	radstrlen_type len)
{
	struct radnode* n = rt->root;
	radstrlen_type pos = 0;
	uint8_t byte;
	while(n) {
		if(pos == len)
			return n->elem?n:NULL;
		byte = k[pos];
		if(byte < n->offset)
			return NULL;
		byte -= n->offset;
		if(byte >= n->len)
			return NULL;
		pos++;
		if(n->array[byte].len != 0) {
			/* must match additional string */
			if(pos+n->array[byte].len > len)
				return NULL; /* no match */
			if(memcmp(&k[pos], n->array[byte].str,
				n->array[byte].len) != 0)
				return NULL; /* no match */
			pos += n->array[byte].len;
		}
		n = n->array[byte].node;
	}
	return NULL;
}

/** return self or a previous element */
static int ret_self_or_prev(struct radnode* n, struct radnode** result)
{
	if(n->elem)
		*result = n;
	else	*result = radix_prev(n);
	return 0;
}

int radix_find_less_equal(struct radtree* rt, uint8_t* k, radstrlen_type len,
        struct radnode** result)
{
	struct radnode* n = rt->root;
	radstrlen_type pos = 0;
	uint8_t byte;
	int r;
	if(!n) {
		/* empty tree */
		*result = NULL;
		return 0;
	}
	while(pos < len) {
		byte = k[pos];
		if(byte < n->offset) {
			/* so the previous is the element itself */
			/* or something before this element */
			return ret_self_or_prev(n, result);
		}
		byte -= n->offset;
		if(byte >= n->len) {
			/* so, the previous is the last of array, or itself */
			/* or something before this element */
			if((*result=radnode_last_in_subtree_incl_self(n))==0)
				*result = radix_prev(n);
			return 0;
		}
		pos++;
		if(!n->array[byte].node) {
			/* no match */
			/* Find an entry in arrays from byte-1 to 0 */
			*result = radnode_find_prev_from_idx(n, byte);
			if(*result)
				return 0;
			/* this entry or something before it */
			return ret_self_or_prev(n, result);
		}
		if(n->array[byte].len != 0) {
			/* must match additional string */
			if(pos+n->array[byte].len > len) {
				/* the additional string is longer than key*/
				if( (memcmp(&k[pos], n->array[byte].str,
					len-pos)) <= 0) {
				  /* and the key is before this node */
				  *result = radix_prev(n->array[byte].node);
				} else {
					/* the key is after the additional
					 * string, thus everything in that
					 * subtree is smaller. */
				  	*result=radnode_last_in_subtree_incl_self(n->array[byte].node);
					/* if somehow that is NULL,
					 * then we have an inefficient tree:
					 * byte+1 is larger than us, so find
					 * something in byte-1 and before */
					if(!*result)
						*result = radix_prev(n->array[byte].node);
				}
				return 0; /* no match */
			}
			if( (r=memcmp(&k[pos], n->array[byte].str,
				n->array[byte].len)) < 0) {
				*result = radix_prev(n->array[byte].node);
				return 0; /* no match */
			} else if(r > 0) {
				/* the key is larger than the additional
				 * string, thus everything in that subtree
				 * is smaller */
				*result=radnode_last_in_subtree_incl_self(n->array[byte].node);
				/* if we have an inefficient tree */
				if(!*result) *result = radix_prev(n->array[byte].node);
				return 0; /* no match */
			}
			pos += n->array[byte].len;
		}
		n = n->array[byte].node;
	}
	if(n->elem) {
		/* exact match */
		*result = n;
		return 1;
	}
	/* there is a node which is an exact match, but it has no element */
	*result = radix_prev(n);
	return 0;
}


struct radnode* radix_first(struct radtree* rt)
{
	struct radnode* n;
	if(!rt || !rt->root) return NULL;
	n = rt->root;
	if(n->elem) return n;
	return radix_next(n);
}

struct radnode* radix_last(struct radtree* rt)
{
	if(!rt || !rt->root) return NULL;
	return radnode_last_in_subtree_incl_self(rt->root);
}

struct radnode* radix_next(struct radnode* n)
{
	if(!n) return NULL;
	if(n->len) {
		/* go down */
		struct radnode* s = radnode_first_in_subtree(n);
		if(s) return s;
	}
	/* go up - the parent->elem is not useful, because it is before us */
	while(n->parent) {
		unsigned idx = n->pidx;
		n = n->parent;
		idx++;
		for(; idx < n->len; idx++) {
			/* go down the next branch */
			if(n->array[idx].node) {
				struct radnode* s;
				/* node itself */
				if(n->array[idx].node->elem)
					return n->array[idx].node;
				/* or subtree */
				s = radnode_first_in_subtree(
					n->array[idx].node);
				if(s) return s;
			}
		}
	}
	return NULL;
}

struct radnode* radix_prev(struct radnode* n)
{
	if(!n) return NULL;
	/* must go up, since all array nodes are after this node */
	while(n->parent) {
		uint8_t idx = n->pidx;
		struct radnode* s;
		n = n->parent;
		assert(n->len > 0); /* since we are a child */
		/* see if there are elements in previous branches there */
		s = radnode_find_prev_from_idx(n, idx);
		if(s) return s;
		/* the current node is before the array */
		if(n->elem)
			return n;
	}
	return NULL;
}

/** convert one character from domain-name to radname */
static uint8_t char_d2r(uint8_t c)
{
	if(c < 'A') return c+1; /* make space for 00 */
	else if(c <= 'Z') return c-'A'+'a'; /* lowercase */
	else return c;
}

/** convert one character from radname to domain-name (still lowercased) */
static uint8_t char_r2d(uint8_t c)
{
	assert(c != 0); /* end of label */
	if(c <= 'A') return c-1;
	else return c;
}

/** copy and convert a range of characters */
static void cpy_d2r(uint8_t* to, const uint8_t* from, int len)
{
	int i;
	for(i=0; i<len; i++)
		to[i] = char_d2r(from[i]);
}

/** copy and convert a range of characters */
static void cpy_r2d(uint8_t* to, uint8_t* from, uint8_t len)
{
	uint8_t i;
	for(i=0; i<len; i++)
		to[i] = char_r2d(from[i]);
}

/* radname code: domain to radix-bstring */
void radname_d2r(uint8_t* k, radstrlen_type* len, const uint8_t* dname,
	size_t dlen)
{
	/* the domain name is converted as follows,
	 * to preserve the normal (NSEC) ordering of domain names.
	 * lowercased, and 'end-of-label' is a '00' byte,
	 * bytes 00-'A' are +1 moved to make space for 00 byte.
	 * final root label is not appended (string ends).
	 * because the only allowed empty label is the final root label,
	 * we can also remove the last 00 label-end.
	 * The total result length is one-or-two less than the dname.
	 * 
	 * examples (numbers are bytes, letters are ascii):
	 * - root: dname: 0, radname: ''
	 * - nl.:  dname: 3nl0, radname: 'nl'
	 * - labs.nl: dname 4labs3nl0, radname: 'nl0labs'
	 * - x.labs.nl: dname 1x4labs3nl0, radname: 'nl0labs0x'
	 */

	/* conversion by putting the label starts on a stack */
	const uint8_t* labstart[130];
	unsigned int lab = 0, kpos, dpos = 0;
	/* sufficient space */
	assert(k && dname);
	assert(dlen <= 256); /* and therefore not more than 128 labels */
	assert(*len >= dlen);
	assert(dlen > 0); /* even root label has dlen=1 */

	/* root */
	if(dlen == 1) {
		assert(dname[0] == 0);
		*len = 0;
		return;
	}
	
	/* walk through domain name and remember label positions */
	do {
		/* compression pointers not allowed */
		if((dname[dpos] & 0xc0)) {
			*len = 0;
			return; /* format error */
		}
		labstart[lab++] = &dname[dpos];
		if(dpos + dname[dpos] + 1 >= dlen) {
			*len = 0;
			return; /* format error */
		}
		/* skip the label contents */
		dpos += dname[dpos];
		dpos ++;
	} while(dname[dpos] != 0);
	/* exit condition makes root label not in labelstart stack */
	/* because the root was handled before, we know there is some text */
	assert(lab > 0);
	lab-=1;
	kpos = *labstart[lab];
	cpy_d2r(k, labstart[lab]+1, kpos);
	/* if there are more labels, copy them over */
	while(lab) {
		/* put 'end-of-label' 00 to end previous label */
		k[kpos++]=0;
		/* append the label */
		lab--;
		cpy_d2r(k+kpos, labstart[lab]+1, *labstart[lab]);
		kpos += *labstart[lab];
	}
	/* done */
	assert(kpos == dlen-2); /* no rootlabel, one less label-marker */
	*len = kpos;
}

/* radname code: radix-bstring to domain */
void radname_r2d(uint8_t* k, radstrlen_type len, uint8_t* dname, size_t* dlen)
{
	/* find labels and push on stack */
	uint8_t* labstart[130];
	uint8_t lablen[130];
	unsigned int lab = 0, dpos, kpos = 0;
	/* sufficient space */
	assert(k && dname);
	assert((size_t)*dlen >= (size_t)len+2);
	assert(len <= 256);
	/* root label */
	if(len == 0) {
		assert(*dlen > 0);
		dname[0]=0;
		*dlen=1;
		return;
	}
	/* find labels */
	while(kpos < len) {
		lablen[lab]=0;
			labstart[lab]=&k[kpos];
		/* skip to next label */
		while(kpos < len && k[kpos] != 0) {
			lablen[lab]++;
			kpos++;
		}
		lab++;
		/* skip 00 byte for label-end */
		if(kpos < len) {
			assert(k[kpos] == 0);
			kpos++;
		}
	}
	/* copy the labels over to the domain name */
	dpos = 0;
	while(lab) {
		lab--;
		/* label length */
		dname[dpos++] = lablen[lab];
		/* label content */
		cpy_r2d(dname+dpos, labstart[lab], lablen[lab]);
		dpos += lablen[lab];
	}
	/* append root label */
	dname[dpos++] = 0;
	/* assert the domain name is wellformed */
	assert((int)dpos == (int)len+2);
	assert(dname[dpos-1] == 0); /* ends with root label */
	*dlen = dpos;
}

/** insert by domain name */
struct radnode*
radname_insert(struct radtree* rt, const uint8_t* d, size_t max, void* elem)
{
	/* convert and insert */
	uint8_t radname[300];
	radstrlen_type len = (radstrlen_type)sizeof(radname);
	if(max > sizeof(radname))
		return NULL; /* too long */
	radname_d2r(radname, &len, d, max);
	return radix_insert(rt, radname, len, elem);
}

/** delete by domain name */
void
radname_delete(struct radtree* rt, const uint8_t* d, size_t max)
{
	/* search and remove */
	struct radnode* n = radname_search(rt, d, max);
	if(n) radix_delete(rt, n);
}

/* search for exact match of domain name, converted to radname in tree */
struct radnode* radname_search(struct radtree* rt, const uint8_t* d,
	size_t max)
{
	/* stack of labels in the domain name */
	const uint8_t* labstart[130];
	unsigned int lab, dpos, lpos;
	struct radnode* n = rt->root;
	uint8_t byte;
	radstrlen_type i;
	uint8_t b;

	/* search for root? it is '' */
	if(max < 1)
		return NULL;
	if(d[0] == 0) {
		if(!n) return NULL;
		return n->elem?n:NULL;
	}

	/* find labels stack in domain name */
	lab = 0;
	dpos = 0;
	/* must have one label, since root is specialcased */
	do {
		if((d[dpos] & 0xc0))
			return NULL; /* compression ptrs not allowed error */
		labstart[lab++] = &d[dpos];
		if(dpos + d[dpos] + 1 >= max)
			return NULL; /* format error: outside of bounds */
		/* skip the label contents */
		dpos += d[dpos];
		dpos ++;
	} while(d[dpos] != 0);
	/* exit condition makes that root label is not in the labstarts */
	/* now: dpos+1 is length of domain name. lab is number of labels-1 */

	/* start processing at the last label */
	lab-=1;
	lpos = 0;
	while(n) {
		/* fetch next byte this label */
		if(lpos < *labstart[lab])
			/* lpos+1 to skip labelstart, lpos++ to move forward */
			byte = char_d2r(labstart[lab][++lpos]);
		else {
			if(lab == 0) /* last label - we're done */
				return n->elem?n:NULL;
			/* next label, search for byte 00 */
			lpos = 0;
			lab--;
			byte = 0;
		}
		/* find that byte in the array */
		if(byte < n->offset)
			return NULL;
		byte -= n->offset;
		if(byte >= n->len)
			return NULL;
		if(n->array[byte].len != 0) {
			/* must match additional string */
			/* see how many bytes we need and start matching them*/
			for(i=0; i<n->array[byte].len; i++) {
				/* next byte to match */
				if(lpos < *labstart[lab])
					b = char_d2r(labstart[lab][++lpos]);
				else {
					/* if last label, no match since
					 * we are in the additional string */
					if(lab == 0)
						return NULL; 
					/* next label, search for byte 00 */
					lpos = 0;
					lab--;
					b = 0;
				}
				if(n->array[byte].str[i] != b)
					return NULL; /* not matched */
			}
		}
		n = n->array[byte].node;
	}
	return NULL;
}

/* find domain name or smaller or equal domain name in radix tree */
int radname_find_less_equal(struct radtree* rt, const uint8_t* d, size_t max,
        struct radnode** result)
{
	/* stack of labels in the domain name */
	const uint8_t* labstart[130];
	unsigned int lab, dpos, lpos;
	struct radnode* n = rt->root;
	uint8_t byte;
	radstrlen_type i;
	uint8_t b;

	/* empty tree */
	if(!n) {
		*result = NULL;
		return 0;
	}

	/* search for root? it is '' */
	if(max < 1) {
		*result = NULL;
		return 0; /* parse error, out of bounds */
	}
	if(d[0] == 0) {
		if(n->elem) {
			*result = n;
			return 1;
		}
		/* no smaller element than the root */
		*result = NULL;
		return 0;
	}

	/* find labels stack in domain name */
	lab = 0;
	dpos = 0;
	/* must have one label, since root is specialcased */
	do {
		if((d[dpos] & 0xc0)) {
			*result = NULL;
			return 0; /* compression ptrs not allowed error */
		}
		labstart[lab++] = &d[dpos];
		if(dpos + d[dpos] + 1 >= max) {
			*result = NULL; /* format error: outside of bounds */
			return 0;
		}
		/* skip the label contents */
		dpos += d[dpos];
		dpos ++;
	} while(d[dpos] != 0);
	/* exit condition makes that root label is not in the labstarts */
	/* now: dpos+1 is length of domain name. lab is number of labels-1 */

	/* start processing at the last label */
	lab-=1;
	lpos = 0;
	while(1) {
		/* fetch next byte this label */
		if(lpos < *labstart[lab])
			/* lpos+1 to skip labelstart, lpos++ to move forward */
			byte = char_d2r(labstart[lab][++lpos]);
		else {
			if(lab == 0) {
				/* last label - we're done */
				/* exact match */
				if(n->elem) {
					*result = n;
					return 1;
				}
				/* there is a node which is an exact match,
				 * but there no element in it */
				*result = radix_prev(n);
				return 0;
			}
			/* next label, search for byte 0 the label separator */
			lpos = 0;
			lab--;
			byte = 0;
		}
		/* find that byte in the array */
		if(byte < n->offset)
			/* so the previous is the element itself */
			/* or something before this element */
			return ret_self_or_prev(n, result);
		byte -= n->offset;
		if(byte >= n->len) {
			/* so, the previous is the last of array, or itself */
			/* or something before this element */
			*result = radnode_last_in_subtree_incl_self(n);
			if(!*result)
				*result = radix_prev(n);
			return 0;
		}
		if(!n->array[byte].node) {
			/* no match */
			/* Find an entry in arrays from byte-1 to 0 */
			*result = radnode_find_prev_from_idx(n, byte);
			if(*result)
				return 0;
			/* this entry or something before it */
			return ret_self_or_prev(n, result);
		}
		if(n->array[byte].len != 0) {
			/* must match additional string */
			/* see how many bytes we need and start matching them*/
			for(i=0; i<n->array[byte].len; i++) {
				/* next byte to match */
				if(lpos < *labstart[lab])
					b = char_d2r(labstart[lab][++lpos]);
				else {
					/* if last label, no match since
					 * we are in the additional string */
					if(lab == 0) {
						/* dname ended, thus before
						 * this array element */
						*result =radix_prev(
							n->array[byte].node);
						return 0; 
					}
					/* next label, search for byte 00 */
					lpos = 0;
					lab--;
					b = 0;
				}
				if(b < n->array[byte].str[i]) {
					*result =radix_prev(
						n->array[byte].node);
					return 0; 
				} else if(b > n->array[byte].str[i]) {
					/* the key is after the additional,
					 * so everything in its subtree is
					 * smaller */
					*result = radnode_last_in_subtree_incl_self(n->array[byte].node);
					/* if that is NULL, we have an
					 * inefficient tree, find in byte-1*/
					if(!*result)
						*result = radix_prev(n->array[byte].node);
					return 0;
				}
			}
		}
		n = n->array[byte].node;
	}
	/* ENOTREACH */
	return 0;
}