root/usr.sbin/nsd/radtree.c
/*
 * 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;
}