diff options
Diffstat (limited to 'usr.sbin/nsd/udbradtree.c')
-rw-r--r-- | usr.sbin/nsd/udbradtree.c | 1463 |
1 files changed, 1463 insertions, 0 deletions
diff --git a/usr.sbin/nsd/udbradtree.c b/usr.sbin/nsd/udbradtree.c new file mode 100644 index 00000000000..d9be6b9c255 --- /dev/null +++ b/usr.sbin/nsd/udbradtree.c @@ -0,0 +1,1463 @@ +/* + * udbradtree -- radix tree for binary strings for in udb file. + * + * Copyright (c) 2011, NLnet Labs. See LICENSE for license. + */ +#include "config.h" +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include "udbradtree.h" +#include "radtree.h" +#define RADARRAY(ptr) ((struct udb_radarray_d*)UDB_PTR(ptr)) + +/** see if radarray can be reduced (by a factor of two) */ +static int udb_radarray_reduce_if_needed(udb_base* udb, udb_ptr* n); + +int udb_radix_tree_create(udb_base* udb, udb_ptr* ptr) +{ + if(!udb_ptr_alloc_space(ptr, udb, udb_chunk_type_radtree, + sizeof(struct udb_radtree_d))) + return 0; + udb_rel_ptr_init(&RADTREE(ptr)->root); + RADTREE(ptr)->count = 0; + return 1; +} + +/** size of radarray */ +static size_t size_of_radarray(struct udb_radarray_d* a) +{ + return sizeof(struct udb_radarray_d)+((size_t)a->capacity)*( + sizeof(struct udb_radsel_d)+(size_t)a->str_cap); +} + +/** size in bytes of data in the array lookup structure */ +static size_t size_of_lookup(udb_ptr* node) +{ + assert(udb_ptr_get_type(node) == udb_chunk_type_radnode); + return size_of_radarray((struct udb_radarray_d*)UDB_REL(*node->base, + RADNODE(node)->lookup.data)); +} + +/** external variant, size in bytes of data in the array lookup structure */ +size_t size_of_lookup_ext(udb_ptr* lookup) +{ + return size_of_lookup(lookup); +} + +/** size needed for a lookup array like this */ +static size_t size_of_lookup_needed(uint16_t capacity, udb_radstrlen_t str_cap) +{ + return sizeof(struct udb_radarray_d)+ ((size_t)capacity)*( + sizeof(struct udb_radsel_d)+(size_t)str_cap); +} + +/** get the lookup array for a node */ +static struct udb_radarray_d* lookup(udb_ptr* n) +{ + assert(udb_ptr_get_type(n) == udb_chunk_type_radnode); + return (struct udb_radarray_d*)UDB_REL(*n->base, + RADNODE(n)->lookup.data); +} + +/** get a length in the lookup array */ +static udb_radstrlen_t lookup_len(udb_ptr* n, unsigned i) +{ + return lookup(n)->array[i].len; +} + +/** get a string in the lookup array */ +static uint8_t* lookup_string(udb_ptr* n, unsigned i) +{ + return ((uint8_t*)&(lookup(n)->array[lookup(n)->capacity]))+ + i*lookup(n)->str_cap; +} + +/** get a node in the lookup array */ +static struct udb_radnode_d* lookup_node(udb_ptr* n, unsigned i) +{ + return (struct udb_radnode_d*)UDB_REL(*n->base, + lookup(n)->array[i].node.data); +} + +/** zero the relptrs in radarray */ +static void udb_radarray_zero_ptrs(udb_base* udb, udb_ptr* n) +{ + unsigned i; + for(i=0; i<lookup(n)->len; i++) { + udb_rptr_zero(&lookup(n)->array[i].node, udb); + } +} + +/** delete a radnode */ +static void udb_radnode_delete(udb_base* udb, udb_ptr* n) +{ + if(udb_ptr_is_null(n)) + return; + if(RADNODE(n)->lookup.data) { + udb_radarray_zero_ptrs(udb, n); + udb_rel_ptr_free_space(&RADNODE(n)->lookup, udb, + size_of_lookup(n)); + } + udb_rptr_zero(&RADNODE(n)->lookup, udb); + udb_rptr_zero(&RADNODE(n)->parent, udb); + udb_rptr_zero(&RADNODE(n)->elem, udb); + udb_ptr_free_space(n, udb, sizeof(struct udb_radnode_d)); +} + +/** delete radnodes in postorder recursion, n is ptr to node */ +static void udb_radnode_del_postorder(udb_base* udb, udb_ptr* n) +{ + unsigned i; + udb_ptr sub; + if(udb_ptr_is_null(n)) + return; + /* clear subnodes */ + udb_ptr_init(&sub, udb); + for(i=0; i<lookup(n)->len; i++) { + udb_ptr_set_rptr(&sub, udb, &lookup(n)->array[i].node); + udb_rptr_zero(&lookup(n)->array[i].node, udb); + udb_radnode_del_postorder(udb, &sub); + } + udb_ptr_unlink(&sub, udb); + /* clear lookup */ + udb_rel_ptr_free_space(&RADNODE(n)->lookup, udb, size_of_lookup(n)); + udb_rptr_zero(&RADNODE(n)->parent, udb); + udb_rptr_zero(&RADNODE(n)->elem, udb); + udb_ptr_free_space(n, udb, sizeof(struct udb_radnode_d)); +} + +void udb_radix_tree_clear(udb_base* udb, udb_ptr* rt) +{ + udb_ptr root; + udb_ptr_new(&root, udb, &RADTREE(rt)->root); + udb_rptr_zero(&RADTREE(rt)->root, udb); + /* free the root node (and its descendants, if any) */ + udb_radnode_del_postorder(udb, &root); + udb_ptr_unlink(&root, udb); + + RADTREE(rt)->count = 0; +} + +void udb_radix_tree_delete(udb_base* udb, udb_ptr* rt) +{ + if(rt->data == 0) return; + assert(udb_ptr_get_type(rt) == udb_chunk_type_radtree); + udb_radix_tree_clear(udb, rt); + udb_ptr_free_space(rt, udb, sizeof(struct udb_radtree_d)); +} + +/** + * 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. Output. + * @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 udb_radix_find_prefix_node(udb_base* udb, udb_ptr* rt, uint8_t* k, + udb_radstrlen_t len, udb_ptr* result, udb_radstrlen_t* respos) +{ + udb_radstrlen_t pos = 0; + uint8_t byte; + udb_ptr n; + udb_ptr_new(&n, udb, &RADTREE(rt)->root); + + *respos = 0; + udb_ptr_set_ptr(result, udb, &n); + if(udb_ptr_is_null(&n)) { + udb_ptr_unlink(&n, udb); + return 0; + } + while(!udb_ptr_is_null(&n)) { + if(pos == len) { + break; + } + byte = k[pos]; + if(byte < RADNODE(&n)->offset) { + break; + } + byte -= RADNODE(&n)->offset; + if(byte >= lookup(&n)->len) { + break; + } + pos++; + if(lookup(&n)->array[byte].len != 0) { + /* must match additional string */ + if(pos+lookup(&n)->array[byte].len > len) { + break; + } + if(memcmp(&k[pos], lookup_string(&n, byte), + lookup(&n)->array[byte].len) != 0) { + break; + } + pos += lookup(&n)->array[byte].len; + } + udb_ptr_set_rptr(&n, udb, &lookup(&n)->array[byte].node); + if(udb_ptr_is_null(&n)) { + break; + } + *respos = pos; + udb_ptr_set_ptr(result, udb, &n); + } + udb_ptr_unlink(&n, udb); + return 1; +} + +/** grow the radnode stringcapacity, copy existing elements */ +static int udb_radnode_str_grow(udb_base* udb, udb_ptr* n, udb_radstrlen_t want) +{ + unsigned ns = ((unsigned)lookup(n)->str_cap)*2; + unsigned i; + udb_ptr a; + if(want > ns) + ns = want; + if(ns > 65535) ns = 65535; /* MAX of udb_radstrlen_t range */ + /* if this fails, the tree is still usable */ + if(!udb_ptr_alloc_space(&a, udb, udb_chunk_type_radarray, + size_of_lookup_needed(lookup(n)->capacity, ns))) + return 0; + /* make sure to zero the newly allocated relptrs to init them */ + memcpy(RADARRAY(&a), lookup(n), sizeof(struct udb_radarray_d)); + RADARRAY(&a)->str_cap = ns; + for(i = 0; i < lookup(n)->len; i++) { + udb_rel_ptr_init(&RADARRAY(&a)->array[i].node); + udb_rptr_set_rptr(&RADARRAY(&a)->array[i].node, udb, + &lookup(n)->array[i].node); + RADARRAY(&a)->array[i].len = lookup_len(n, i); + memmove(((uint8_t*)(&RADARRAY(&a)->array[ + lookup(n)->capacity]))+i*ns, + lookup_string(n, i), lookup(n)->str_cap); + } + udb_radarray_zero_ptrs(udb, n); + udb_rel_ptr_free_space(&RADNODE(n)->lookup, udb, size_of_lookup(n)); + udb_rptr_set_ptr(&RADNODE(n)->lookup, udb, &a); + udb_ptr_unlink(&a, udb); + return 1; +} + +/** grow the radnode array, copy existing elements to start of new array */ +static int udb_radnode_array_grow(udb_base* udb, udb_ptr* n, size_t want) +{ + unsigned i; + unsigned ns = ((unsigned)lookup(n)->capacity)*2; + udb_ptr a; + assert(want <= 256); /* cannot be more, range of uint8 */ + if(want > ns) + ns = want; + if(ns > 256) ns = 256; + /* if this fails, the tree is still usable */ + if(!udb_ptr_alloc_space(&a, udb, udb_chunk_type_radarray, + size_of_lookup_needed(ns, lookup(n)->str_cap))) + return 0; + /* zero the newly allocated rel ptrs to init them */ + memset(UDB_PTR(&a), 0, size_of_lookup_needed(ns, lookup(n)->str_cap)); + assert(lookup(n)->len <= lookup(n)->capacity); + assert(lookup(n)->capacity < ns); + memcpy(RADARRAY(&a), lookup(n), sizeof(struct udb_radarray_d)); + RADARRAY(&a)->capacity = ns; + for(i=0; i<lookup(n)->len; i++) { + udb_rptr_set_rptr(&RADARRAY(&a)->array[i].node, udb, + &lookup(n)->array[i].node); + RADARRAY(&a)->array[i].len = lookup_len(n, i); + } + memmove(&RADARRAY(&a)->array[ns], lookup_string(n, 0), + lookup(n)->len * lookup(n)->str_cap); + udb_radarray_zero_ptrs(udb, n); + udb_rel_ptr_free_space(&RADNODE(n)->lookup, udb, size_of_lookup(n)); + udb_rptr_set_ptr(&RADNODE(n)->lookup, udb, &a); + udb_ptr_unlink(&a, udb); + return 1; +} + +/** make empty array in radnode */ +static int udb_radnode_array_create(udb_base* udb, udb_ptr* n) +{ + /* is there an array? */ + if(RADNODE(n)->lookup.data == 0) { + /* create array */ + udb_ptr a; + uint16_t cap = 0; + udb_radstrlen_t len = 0; + if(!udb_ptr_alloc_space(&a, udb, udb_chunk_type_radarray, + size_of_lookup_needed(cap, len))) + return 0; + memset(UDB_PTR(&a), 0, size_of_lookup_needed(cap, len)); + udb_rptr_set_ptr(&RADNODE(n)->lookup, udb, &a); + RADARRAY(&a)->len = cap; + RADARRAY(&a)->capacity = cap; + RADARRAY(&a)->str_cap = len; + RADNODE(n)->offset = 0; + udb_ptr_unlink(&a, udb); + } + return 1; +} + +/** make space in radnode for another byte, or longer strings */ +static int udb_radnode_array_space(udb_base* udb, udb_ptr* n, uint8_t byte, + udb_radstrlen_t len) +{ + /* is there an array? */ + if(RADNODE(n)->lookup.data == 0) { + /* create array */ + udb_ptr a; + uint16_t cap = 1; + if(!udb_ptr_alloc_space(&a, udb, udb_chunk_type_radarray, + size_of_lookup_needed(cap, len))) + return 0; + /* this memset inits the relptr that is allocated */ + memset(UDB_PTR(&a), 0, size_of_lookup_needed(cap, len)); + udb_rptr_set_ptr(&RADNODE(n)->lookup, udb, &a); + RADARRAY(&a)->len = cap; + RADARRAY(&a)->capacity = cap; + RADARRAY(&a)->str_cap = len; + RADNODE(n)->offset = byte; + udb_ptr_unlink(&a, udb); + return 1; + } + if(lookup(n)->capacity == 0) { + if(!udb_radnode_array_grow(udb, n, 1)) + return 0; + } + + /* make space for this stringsize */ + if(lookup(n)->str_cap < len) { + /* must resize for stringsize */ + if(!udb_radnode_str_grow(udb, n, len)) + return 0; + } + + /* other cases */ + /* is the array unused? */ + if(lookup(n)->len == 0 && lookup(n)->capacity != 0) { + lookup(n)->len = 1; + RADNODE(n)->offset = byte; + memset(&lookup(n)->array[0], 0, sizeof(struct udb_radsel_d)); + /* is it below the offset? */ + } else if(byte < RADNODE(n)->offset) { + /* is capacity enough? */ + int i; + unsigned need = RADNODE(n)->offset-byte; + if(lookup(n)->len+need > lookup(n)->capacity) { + /* grow array */ + if(!udb_radnode_array_grow(udb, n, lookup(n)->len+need)) + return 0; + } + /* take a piece of capacity into use, init the relptrs */ + for(i = lookup(n)->len; i< (int)(lookup(n)->len + need); i++) { + udb_rel_ptr_init(&lookup(n)->array[i].node); + } + /* reshuffle items to end */ + for(i = lookup(n)->len-1; i >= 0; i--) { + udb_rptr_set_rptr(&lookup(n)->array[need+i].node, + udb, &lookup(n)->array[i].node); + lookup(n)->array[need+i].len = lookup_len(n, i); + /* fixup pidx */ + if(lookup(n)->array[i+need].node.data) + lookup_node(n, i+need)->pidx = i+need; + } + memmove(lookup_string(n, need), lookup_string(n, 0), + lookup(n)->len*lookup(n)->str_cap); + /* zero the first */ + for(i = 0; i < (int)need; i++) { + udb_rptr_zero(&lookup(n)->array[i].node, udb); + lookup(n)->array[i].len = 0; + } + lookup(n)->len += need; + RADNODE(n)->offset = byte; + /* is it above the max? */ + } else if(byte - RADNODE(n)->offset >= lookup(n)->len) { + /* is capacity enough? */ + int i; + unsigned need = (byte-RADNODE(n)->offset) - lookup(n)->len + 1; + /* grow array */ + if(lookup(n)->len + need > lookup(n)->capacity) { + if(!udb_radnode_array_grow(udb, n, lookup(n)->len+need)) + return 0; + } + /* take new entries into use, init relptrs */ + for(i = lookup(n)->len; i< (int)(lookup(n)->len + need); i++) { + udb_rel_ptr_init(&lookup(n)->array[i].node); + lookup(n)->array[i].len = 0; + } + /* grow length */ + lookup(n)->len += need; + } + return 1; +} + +/** make space for string size */ +static int udb_radnode_str_space(udb_base* udb, udb_ptr* n, udb_radstrlen_t len) +{ + if(RADNODE(n)->lookup.data == 0) { + return udb_radnode_array_space(udb, n, 0, len); + } + if(lookup(n)->str_cap < len) { + /* must resize for stringsize */ + if(!udb_radnode_str_grow(udb, n, len)) + return 0; + } + return 1; +} + +/** copy remainder from prefixes for a split: + * plen: len prefix, l: longer bstring, llen: length of l. */ +static void udb_radsel_prefix_remainder(udb_radstrlen_t plen, + uint8_t* l, udb_radstrlen_t llen, + uint8_t* s, udb_radstrlen_t* slen) +{ + *slen = llen - plen; + /* assert(*slen <= lookup(n)->str_cap); */ + memmove(s, l+plen, llen-plen); +} + +/** create a prefix in the array strs */ +static void udb_radsel_str_create(uint8_t* s, udb_radstrlen_t* slen, + uint8_t* k, udb_radstrlen_t pos, udb_radstrlen_t len) +{ + *slen = len-pos; + /* assert(*slen <= lookup(n)->str_cap); */ + memmove(s, k+pos, len-pos); +} + +static udb_radstrlen_t +udb_bstr_common(uint8_t* x, udb_radstrlen_t xlen, + uint8_t* y, udb_radstrlen_t ylen) +{ + assert(sizeof(radstrlen_t) == sizeof(udb_radstrlen_t)); + return bstr_common_ext(x, xlen, y, ylen); +} + +static int +udb_bstr_is_prefix(uint8_t* p, udb_radstrlen_t plen, + uint8_t* x, udb_radstrlen_t xlen) +{ + assert(sizeof(radstrlen_t) == sizeof(udb_radstrlen_t)); + return bstr_is_prefix_ext(p, plen, x, xlen); +} + +/** grow array space for byte N after a string, (but if string shorter) */ +static int +udb_radnode_array_space_strremain(udb_base* udb, udb_ptr* n, + uint8_t* str, udb_radstrlen_t len, udb_radstrlen_t pos) +{ + assert(pos < len); + /* shift by one char because it goes in lookup array */ + return udb_radnode_array_space(udb, n, str[pos], len-(pos+1)); +} + + +/** radsel create a split when two nodes have shared prefix. + * @param udb: udb + * @param n: node with the radsel that gets changed, it contains a node. + * @param idx: the index of the radsel that gets changed. + * @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 udb_radsel_split(udb_base* udb, udb_ptr* n, uint8_t idx, uint8_t* k, + udb_radstrlen_t pos, udb_radstrlen_t len, udb_ptr* add) +{ + uint8_t* addstr = k+pos; + udb_radstrlen_t addlen = len-pos; + if(udb_bstr_is_prefix(addstr, addlen, lookup_string(n, idx), + lookup_len(n, idx))) { + udb_radstrlen_t 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 != lookup_len(n, idx)); + assert(addlen < lookup_len(n, idx)); + /* make space for new string sizes */ + if(!udb_radnode_str_space(udb, n, addlen)) + return 0; + if(lookup_len(n, idx) - addlen > 1) + /* shift one because a char is in the lookup array */ + split_len = lookup_len(n, idx) - (addlen+1); + if(!udb_radnode_array_space(udb, add, + lookup_string(n, idx)[addlen], split_len)) + return 0; + /* alloc succeeded, now link it in */ + udb_rptr_set_rptr(&RADNODE(add)->parent, udb, + &lookup_node(n, idx)->parent); + RADNODE(add)->pidx = lookup_node(n, idx)->pidx; + udb_rptr_set_rptr(&lookup(add)->array[0].node, udb, + &lookup(n)->array[idx].node); + if(lookup_len(n, idx) - addlen > 1) { + udb_radsel_prefix_remainder(addlen+1, + lookup_string(n, idx), lookup_len(n, idx), + lookup_string(add, 0), + &lookup(add)->array[0].len); + } else { + lookup(add)->array[0].len = 0; + } + udb_rptr_set_ptr(&lookup_node(n, idx)->parent, udb, add); + lookup_node(n, idx)->pidx = 0; + + udb_rptr_set_ptr(&lookup(n)->array[idx].node, udb, add); + memmove(lookup_string(n, idx), addstr, addlen); + lookup(n)->array[idx].len = addlen; + /* n's string may have become shorter */ + if(!udb_radarray_reduce_if_needed(udb, n)) { + /* ignore this, our tree has become inefficient */ + } + } else if(udb_bstr_is_prefix(lookup_string(n, idx), lookup_len(n, idx), + addstr, addlen)) { + udb_radstrlen_t split_len = 0; + udb_ptr rnode; + /* 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 != lookup_len(n, idx)); + assert(lookup_len(n, idx) < addlen); + udb_ptr_new(&rnode, udb, &lookup(n)->array[idx].node); + /* make space for string length */ + if(addlen-lookup_len(n, idx) > 1) { + /* shift one because a character goes into array */ + split_len = addlen - (lookup_len(n, idx)+1); + } + if(!udb_radnode_array_space(udb, &rnode, + addstr[lookup_len(n, idx)], split_len)) { + udb_ptr_unlink(&rnode, udb); + return 0; + } + /* alloc succeeded, now link it in */ + udb_rptr_set_ptr(&RADNODE(add)->parent, udb, &rnode); + RADNODE(add)->pidx = addstr[lookup_len(n, idx)] - + RADNODE(&rnode)->offset; + udb_rptr_set_ptr(&lookup(&rnode)->array[ RADNODE(add)->pidx ] + .node, udb, add); + if(addlen-lookup_len(n, idx) > 1) { + udb_radsel_prefix_remainder(lookup_len(n, idx)+1, + addstr, addlen, + lookup_string(&rnode, RADNODE(add)->pidx), + &lookup(&rnode)->array[ RADNODE(add)->pidx] + .len); + } else { + lookup(&rnode)->array[ RADNODE(add)->pidx].len = 0; + } + /* rnode's string has become shorter */ + if(!udb_radarray_reduce_if_needed(udb, &rnode)) { + /* ignore this, our tree has become inefficient */ + } + udb_ptr_unlink(&rnode, udb); + } 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. */ + udb_ptr com, rnode; + udb_radstrlen_t common_len = udb_bstr_common( + lookup_string(n, idx), lookup_len(n, idx), + addstr, addlen); + assert(common_len < lookup_len(n, idx)); + assert(common_len < addlen); + udb_ptr_new(&rnode, udb, &lookup(n)->array[idx].node); + + /* create the new node for choice */ + if(!udb_ptr_alloc_space(&com, udb, udb_chunk_type_radnode, + sizeof(struct udb_radnode_d))) { + udb_ptr_unlink(&rnode, udb); + return 0; /* out of space */ + } + memset(UDB_PTR(&com), 0, sizeof(struct udb_radnode_d)); + /* make stringspace for the two substring choices */ + /* this allocates the com->lookup array */ + if(!udb_radnode_array_space_strremain(udb, &com, + lookup_string(n, idx), lookup_len(n, idx), common_len) + || !udb_radnode_array_space_strremain(udb, &com, + addstr, addlen, common_len)) { + udb_ptr_unlink(&rnode, udb); + udb_radnode_delete(udb, &com); + return 0; + } + /* create stringspace for the shared prefix */ + if(common_len > 0) { + if(!udb_radnode_str_space(udb, n, common_len-1)) { + udb_ptr_unlink(&rnode, udb); + udb_radnode_delete(udb, &com); + return 0; + } + } + /* allocs succeeded, proceed to link it all up */ + udb_rptr_set_rptr(&RADNODE(&com)->parent, udb, + &RADNODE(&rnode)->parent); + RADNODE(&com)->pidx = RADNODE(&rnode)->pidx; + udb_rptr_set_ptr(&RADNODE(&rnode)->parent, udb, &com); + RADNODE(&rnode)->pidx = lookup_string(n, idx)[common_len] - + RADNODE(&com)->offset; + udb_rptr_set_ptr(&RADNODE(add)->parent, udb, &com); + RADNODE(add)->pidx = addstr[common_len] - + RADNODE(&com)->offset; + udb_rptr_set_ptr(&lookup(&com)->array[RADNODE(&rnode)->pidx] + .node, udb, &rnode); + if(lookup_len(n, idx)-common_len > 1) { + udb_radsel_prefix_remainder(common_len+1, + lookup_string(n, idx), lookup_len(n, idx), + lookup_string(&com, RADNODE(&rnode)->pidx), + &lookup(&com)->array[RADNODE(&rnode)->pidx].len); + } else { + lookup(&com)->array[RADNODE(&rnode)->pidx].len= 0; + } + udb_rptr_set_ptr(&lookup(&com)->array[RADNODE(add)->pidx] + .node, udb, add); + if(addlen-common_len > 1) { + udb_radsel_prefix_remainder(common_len+1, + addstr, addlen, + lookup_string(&com, RADNODE(add)->pidx), + &lookup(&com)->array[RADNODE(add)->pidx].len); + } else { + lookup(&com)->array[RADNODE(add)->pidx].len = 0; + } + memmove(lookup_string(n, idx), addstr, common_len); + lookup(n)->array[idx].len = common_len; + udb_rptr_set_ptr(&lookup(n)->array[idx].node, udb, &com); + udb_ptr_unlink(&rnode, udb); + udb_ptr_unlink(&com, udb); + /* n's string has become shorter */ + if(!udb_radarray_reduce_if_needed(udb, n)) { + /* ignore this, our tree has become inefficient */ + } + } + return 1; +} + +uint64_t* result_data = NULL; +udb_void udb_radix_insert(udb_base* udb, udb_ptr* rt, uint8_t* k, + udb_radstrlen_t len, udb_ptr* elem, udb_ptr* result) +{ + udb_void ret; + udb_ptr add, n; /* type udb_radnode_d */ + udb_radstrlen_t pos = 0; + /* create new element to add */ + if(!udb_ptr_alloc_space(&add, udb, udb_chunk_type_radnode, + sizeof(struct udb_radnode_d))) { + return 0; /* alloc failure */ + } + memset(UDB_PTR(&add), 0, sizeof(struct udb_radnode_d)); + udb_rptr_set_ptr(&RADNODE(&add)->elem, udb, elem); + if(!udb_radnode_array_create(udb, &add)) { + udb_ptr_free_space(&add, udb, sizeof(struct udb_radnode_d)); + return 0; /* alloc failure */ + } + udb_ptr_init(&n, udb); + result_data = &n.data; + + /* find out where to add it */ + if(!udb_radix_find_prefix_node(udb, rt, k, len, &n, &pos)) { + /* new root */ + assert(RADTREE(rt)->root.data == 0); + if(len == 0) { + udb_rptr_set_ptr(&RADTREE(rt)->root, udb, &add); + } else { + /* add a root to point to new node */ + udb_ptr_zero(&n, udb); + if(!udb_ptr_alloc_space(&n, udb, + udb_chunk_type_radnode, + sizeof(struct udb_radnode_d))) { + udb_radnode_delete(udb, &add); + udb_ptr_unlink(&n, udb); + return 0; /* alloc failure */ + } + memset(RADNODE(&n), 0, sizeof(struct udb_radnode_d)); + /* this creates the array lookup structure for n */ + if(!udb_radnode_array_space(udb, &n, k[0], len-1)) { + udb_radnode_delete(udb, &add); + udb_ptr_free_space(&n, udb, + sizeof(struct udb_radnode_d)); + return 0; /* alloc failure */ + } + udb_rptr_set_ptr(&RADNODE(&add)->parent, udb, &n); + RADNODE(&add)->pidx = 0; + udb_rptr_set_ptr(&lookup(&n)->array[0].node, udb, &add); + if(len > 1) { + udb_radsel_prefix_remainder(1, k, len, + lookup_string(&n, 0), + &lookup(&n)->array[0].len); + } + udb_rptr_set_ptr(&RADTREE(rt)->root, udb, &n); + } + } else if(pos == len) { + /* found an exact match */ + if(RADNODE(&n)->elem.data) { + /* already exists, failure */ + udb_radnode_delete(udb, &add); + udb_ptr_unlink(&n, udb); + return 0; + } + udb_rptr_set_ptr(&RADNODE(&n)->elem, udb, elem); + udb_radnode_delete(udb, &add); + udb_ptr_set_ptr(&add, udb, &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 < RADNODE(&n)->offset || byte-RADNODE(&n)->offset >= + lookup(&n)->len) { + /* make space in the array for it; adjusts offset */ + if(!udb_radnode_array_space(udb, &n, byte, + len-(pos+1))) { + udb_radnode_delete(udb, &add); + udb_ptr_unlink(&n, udb); + return 0; + } + assert(byte>=RADNODE(&n)->offset && byte-RADNODE(&n)-> + offset<lookup(&n)->len); + byte -= RADNODE(&n)->offset; + /* see if more prefix needs to be split off */ + if(pos+1 < len) { + udb_radsel_str_create(lookup_string(&n, byte), + &lookup(&n)->array[byte].len, + k, pos+1, len); + } + /* insert the new node in the new bucket */ + udb_rptr_set_ptr(&RADNODE(&add)->parent, udb, &n); + RADNODE(&add)->pidx = byte; + udb_rptr_set_ptr(&lookup(&n)->array[byte].node, udb, + &add); + /* so a bucket exists and byte falls in it */ + } else if(lookup(&n)->array[byte - RADNODE(&n)->offset] + .node.data == 0) { + /* use existing bucket */ + byte -= RADNODE(&n)->offset; + if(pos+1 < len) { + /* make space and split off more prefix */ + if(!udb_radnode_str_space(udb, &n, + len-(pos+1))) { + udb_radnode_delete(udb, &add); + udb_ptr_unlink(&n, udb); + return 0; + } + udb_radsel_str_create(lookup_string(&n, byte), + &lookup(&n)->array[byte].len, + k, pos+1, len); + } + /* insert the new node in the new bucket */ + udb_rptr_set_ptr(&RADNODE(&add)->parent, udb, &n); + RADNODE(&add)->pidx = byte; + udb_rptr_set_ptr(&lookup(&n)->array[byte].node, udb, + &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(!udb_radsel_split(udb, &n, byte-RADNODE(&n)->offset, + k, pos+1, len, &add)) { + udb_radnode_delete(udb, &add); + udb_ptr_unlink(&n, udb); + return 0; + } + } + } + RADTREE(rt)->count ++; + ret = add.data; + udb_ptr_init(result, udb); + udb_ptr_set_ptr(result, udb, &add); + udb_ptr_unlink(&add, udb); + udb_ptr_unlink(&n, udb); + return ret; +} + +/** Cleanup node with one child, it is removed and joined into parent[x] str */ +static int +udb_radnode_cleanup_onechild(udb_base* udb, udb_ptr* n) +{ + udb_ptr par, child; + uint8_t pidx = RADNODE(n)->pidx; + radstrlen_t joinlen; + udb_ptr_new(&par, udb, &RADNODE(n)->parent); + udb_ptr_new(&child, udb, &lookup(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 < lookup(&par)->len); + joinlen = lookup_len(&par, pidx) + lookup_len(n, 0) + 1; + /* make stringspace for the joined string */ + if(!udb_radnode_str_space(udb, &par, joinlen)) { + /* cleanup failed due to out of memory */ + /* the tree is inefficient, with node n still existing */ + udb_ptr_unlink(&par, udb); + udb_ptr_unlink(&child, udb); + udb_ptr_zero(n, udb); + return 0; + } + /* the string(par, pidx) is already there */ + /* the array lookup is gone, put its character in the lookup string*/ + lookup_string(&par, pidx)[lookup_len(&par, pidx)] = + RADNODE(&child)->pidx + RADNODE(n)->offset; + memmove(lookup_string(&par, pidx)+lookup_len(&par, pidx)+1, + lookup_string(n, 0), lookup_len(n, 0)); + lookup(&par)->array[pidx].len = joinlen; + /* and set the node to our child. */ + udb_rptr_set_ptr(&lookup(&par)->array[pidx].node, udb, &child); + udb_rptr_set_ptr(&RADNODE(&child)->parent, udb, &par); + RADNODE(&child)->pidx = pidx; + /* we are unlinked, delete our node */ + udb_radnode_delete(udb, n); + udb_ptr_unlink(&par, udb); + udb_ptr_unlink(&child, udb); + udb_ptr_zero(n, udb); + return 1; +} + +/** reduce the size of radarray, does a malloc */ +static int +udb_radarray_reduce(udb_base* udb, udb_ptr* n, uint16_t cap, + udb_radstrlen_t strcap) +{ + udb_ptr a; + unsigned i; + assert(lookup(n)->len <= cap); + assert(cap <= lookup(n)->capacity); + assert(strcap <= lookup(n)->str_cap); + if(!udb_ptr_alloc_space(&a, udb, udb_chunk_type_radarray, + size_of_lookup_needed(cap, strcap))) + return 0; + memset(RADARRAY(&a), 0, size_of_lookup_needed(cap, strcap)); + memcpy(RADARRAY(&a), lookup(n), sizeof(struct udb_radarray_d)); + RADARRAY(&a)->capacity = cap; + RADARRAY(&a)->str_cap = strcap; + for(i=0; i<lookup(n)->len; i++) { + udb_rel_ptr_init(&RADARRAY(&a)->array[i].node); + udb_rptr_set_rptr(&RADARRAY(&a)->array[i].node, udb, + &lookup(n)->array[i].node); + RADARRAY(&a)->array[i].len = lookup_len(n, i); + memmove(((uint8_t*)(&RADARRAY(&a)->array[cap]))+i*strcap, + lookup_string(n, i), lookup_len(n, i)); + } + udb_radarray_zero_ptrs(udb, n); + udb_rel_ptr_free_space(&RADNODE(n)->lookup, udb, size_of_lookup(n)); + udb_rptr_set_ptr(&RADNODE(n)->lookup, udb, &a); + udb_ptr_unlink(&a, udb); + return 1; +} + +/** find the max stringlength in the array */ +static udb_radstrlen_t udb_radarray_max_len(udb_ptr* n) +{ + unsigned i; + udb_radstrlen_t maxlen = 0; + for(i=0; i<lookup(n)->len; i++) { + if(lookup(n)->array[i].node.data && + lookup(n)->array[i].len > maxlen) + maxlen = lookup(n)->array[i].len; + } + return maxlen; +} + +/** see if radarray can be reduced (by a factor of two) */ +static int +udb_radarray_reduce_if_needed(udb_base* udb, udb_ptr* n) +{ + udb_radstrlen_t maxlen = udb_radarray_max_len(n); + if((lookup(n)->len <= lookup(n)->capacity/2 || lookup(n)->len == 0 + || maxlen <= lookup(n)->str_cap/2 || maxlen == 0) && + (lookup(n)->len != lookup(n)->capacity || + lookup(n)->str_cap != maxlen)) + return udb_radarray_reduce(udb, n, lookup(n)->len, maxlen); + return 1; +} + +static int +udb_radnode_array_clean_all(udb_base* udb, udb_ptr* n) +{ + RADNODE(n)->offset = 0; + lookup(n)->len = 0; + /* reallocate lookup to a smaller capacity structure */ + return udb_radarray_reduce(udb, n, 0, 0); +} + +/** remove NULL nodes from front of array */ +static int +udb_radnode_array_clean_front(udb_base* udb, udb_ptr* n) +{ + /* move them up and adjust offset */ + unsigned idx, shuf = 0; + /* remove until a nonNULL entry */ + while(shuf < lookup(n)->len && lookup(n)->array[shuf].node.data == 0) + shuf++; + if(shuf == 0) + return 1; + if(shuf == lookup(n)->len) { + /* the array is empty, the tree is inefficient */ + return udb_radnode_array_clean_all(udb, n); + } + assert(shuf < lookup(n)->len); + assert((int)shuf <= 255-(int)RADNODE(n)->offset); + /* move them */ + for(idx=0; idx<lookup(n)->len-shuf; idx++) { + udb_rptr_set_rptr(&lookup(n)->array[idx].node, udb, + &lookup(n)->array[shuf+idx].node); + lookup(n)->array[idx].len = lookup_len(n, shuf+idx); + memmove(lookup_string(n, idx), lookup_string(n, shuf+idx), + lookup(n)->array[idx].len); + } + /* zero the to-be-unused entries */ + for(idx=lookup(n)->len-shuf; idx<lookup(n)->len; idx++) { + udb_rptr_zero(&lookup(n)->array[idx].node, udb); + memset(lookup_string(n, idx), 0, lookup(n)->array[idx].len); + lookup(n)->array[idx].len = 0; + } + RADNODE(n)->offset += shuf; + lookup(n)->len -= shuf; + for(idx=0; idx<lookup(n)->len; idx++) + if(lookup(n)->array[idx].node.data) + lookup_node(n, idx)->pidx = idx; + + /* see if capacity has to shrink */ + return udb_radarray_reduce_if_needed(udb, n); +} + +/** remove NULL nodes from end of array */ +static int +udb_radnode_array_clean_end(udb_base* udb, udb_ptr* n) +{ + /* shorten it */ + unsigned shuf = 0; + /* remove until a nonNULL entry */ + /* remove until a nonNULL entry */ + while(shuf < lookup(n)->len && lookup(n)->array[lookup(n)->len-1-shuf] + .node.data == 0) + shuf++; + if(shuf == 0) + return 1; + if(shuf == lookup(n)->len) { + /* the array is empty, the tree is inefficient */ + return udb_radnode_array_clean_all(udb, n); + } + assert(shuf < lookup(n)->len); + lookup(n)->len -= shuf; + /* array elements can stay where they are */ + /* see if capacity has to shrink */ + return udb_radarray_reduce_if_needed(udb, n); +} + +/** clean up radnode leaf, where we know it has a parent */ +static int +udb_radnode_cleanup_leaf(udb_base* udb, udb_ptr* n, udb_ptr* par) +{ + uint8_t pidx; + /* node was a leaf */ + + /* delete leaf node, but store parent+idx */ + pidx = RADNODE(n)->pidx; + assert(pidx < lookup(par)->len); + + /** set parent ptr to this node to NULL before deleting the node, + * because otherwise ptrlinks fail */ + udb_rptr_zero(&lookup(par)->array[pidx].node, udb); + + udb_radnode_delete(udb, n); + + /* set parent+idx entry to NULL str and node.*/ + lookup(par)->array[pidx].len = 0; + + /* see if par offset or len must be adjusted */ + if(lookup(par)->len == 1) { + /* removed final element from array */ + if(!udb_radnode_array_clean_all(udb, par)) + return 0; + } else if(pidx == 0) { + /* removed first element from array */ + if(!udb_radnode_array_clean_front(udb, par)) + return 0; + } else if(pidx == lookup(par)->len-1) { + /* removed last element from array */ + if(!udb_radnode_array_clean_end(udb, par)) + return 0; + } + return 1; +} + +/** + * Cleanup a radix node that was made smaller, see if it can + * be merged with others. + * @param udb: the udb + * @param rt: tree to remove root if needed. + * @param n: node to cleanup + * @return false on alloc failure. + */ +static int +udb_radnode_cleanup(udb_base* udb, udb_ptr* rt, udb_ptr* n) +{ + while(!udb_ptr_is_null(n)) { + if(RADNODE(n)->elem.data) { + /* see if if needs to be reduced in stringsize */ + if(!udb_radarray_reduce_if_needed(udb, n)) { + udb_ptr_zero(n, udb); + return 0; + } + /* cannot delete node with a data element */ + udb_ptr_zero(n, udb); + return 1; + } else if(lookup(n)->len == 1 && RADNODE(n)->parent.data) { + return udb_radnode_cleanup_onechild(udb, n); + } else if(lookup(n)->len == 0) { + udb_ptr par; + if(!RADNODE(n)->parent.data) { + /* root deleted */ + udb_rptr_zero(&RADTREE(rt)->root, udb); + udb_radnode_delete(udb, n); + return 1; + } + udb_ptr_new(&par, udb, &RADNODE(n)->parent); + /* remove and delete the leaf node */ + if(!udb_radnode_cleanup_leaf(udb, n, &par)) { + udb_ptr_unlink(&par, udb); + udb_ptr_zero(n, udb); + return 0; + } + /* see if parent can now be cleaned up */ + udb_ptr_set_ptr(n, udb, &par); + udb_ptr_unlink(&par, udb); + } else { + /* see if if needs to be reduced in stringsize */ + if(!udb_radarray_reduce_if_needed(udb, n)) { + udb_ptr_zero(n, udb); + return 0; + } + /* node cannot be cleaned up */ + udb_ptr_zero(n, udb); + return 1; + } + } + /* ENOTREACH */ + return 1; +} + +void udb_radix_delete(udb_base* udb, udb_ptr* rt, udb_ptr* n) +{ + if(udb_ptr_is_null(n)) + return; + udb_rptr_zero(&RADNODE(n)->elem, udb); + RADTREE(rt)->count --; + if(!udb_radnode_cleanup(udb, rt, n)) { + /* out of memory in cleanup. the elem ptr is NULL, but + * the radix tree could be inefficient. */ + } +} + +udb_void udb_radix_search(udb_ptr* rt, uint8_t* k, udb_radstrlen_t len) +{ + /* since we only perform reads, and no udb_mallocs or udb_frees + * we know the pointers stay the same */ + struct udb_radnode_d* n; + udb_radstrlen_t pos = 0; + uint8_t byte; + void* base = *rt->base; + + n = (struct udb_radnode_d*)UDB_REL(base, RADTREE(rt)->root.data); +#define NARRAY(n) ((struct udb_radarray_d*)UDB_REL(base, n->lookup.data)) +#define NSTR(n, byte) (((uint8_t*)(&NARRAY(n)->array[NARRAY(n)->capacity]))+byte*NARRAY(n)->str_cap) + while(n != *rt->base) { + if(pos == len) + return UDB_SYSTOREL(*rt->base, n); + byte = k[pos]; + if(byte < n->offset) + return 0; + byte -= n->offset; + if(byte >= NARRAY(n)->len) + return 0; + pos++; + if(NARRAY(n)->array[byte].len != 0) { + /* must match additional string */ + if(pos+NARRAY(n)->array[byte].len > len) + return 0; /* no match */ + if(memcmp(&k[pos], NSTR(n, byte), + NARRAY(n)->array[byte].len) != 0) + return 0; /* no match */ + pos += NARRAY(n)->array[byte].len; + } + n = (struct udb_radnode_d*)UDB_REL(base, + NARRAY(n)->array[byte].node.data); + } + return 0; +} + +/** go to last elem-containing node in this subtree (excl self) */ +static void +udb_radnode_last_in_subtree(udb_base* udb, udb_ptr* n) +{ + int idx; + /* try last entry in array first */ + for(idx=((int)lookup(n)->len)-1; idx >= 0; idx--) { + if(lookup(n)->array[idx].node.data) { + udb_ptr s; + udb_ptr_init(&s, udb); + udb_ptr_set_rptr(&s, udb, &lookup(n)->array[idx].node); + /* does it have entries in its subtrees? */ + if(lookup(&s)->len > 0) { + udb_radnode_last_in_subtree(udb, &s); + if(!udb_ptr_is_null(&s)) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + } + udb_ptr_set_rptr(&s, udb, &lookup(n)->array[idx].node); + /* no, does it have an entry itself? */ + if(RADNODE(&s)->elem.data) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + udb_ptr_unlink(&s, udb); + } + } + udb_ptr_zero(n, udb); +} + +/** last in subtree, incl self */ +static void +udb_radnode_last_in_subtree_incl_self(udb_base* udb, udb_ptr* n) +{ + udb_ptr self; + udb_ptr_init(&self, udb); + udb_ptr_set_ptr(&self, udb, n); + udb_radnode_last_in_subtree(udb, n); + if(!udb_ptr_is_null(n)) { + udb_ptr_unlink(&self, udb); + return; + } + if(RADNODE(&self)->elem.data) { + udb_ptr_set_ptr(n, udb, &self); + udb_ptr_unlink(&self, udb); + return; + } + udb_ptr_zero(n, udb); + udb_ptr_unlink(&self, udb); +} + +/** return first elem-containing node in this subtree (excl self) */ +static void +udb_radnode_first_in_subtree(udb_base* udb, udb_ptr* n) +{ + unsigned idx; + /* try every subnode */ + for(idx=0; idx<lookup(n)->len; idx++) { + if(lookup(n)->array[idx].node.data) { + udb_ptr s; + udb_ptr_init(&s, udb); + udb_ptr_set_rptr(&s, udb, &lookup(n)->array[idx].node); + /* does it have elem itself? */ + if(RADNODE(&s)->elem.data) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + /* try its subtrees */ + udb_radnode_first_in_subtree(udb, &s); + if(!udb_ptr_is_null(&s)) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + + } + } + udb_ptr_zero(n, udb); +} + +/** Find an entry in arrays from idx-1 to 0 */ +static void +udb_radnode_find_prev_from_idx(udb_base* udb, udb_ptr* n, unsigned from) +{ + unsigned idx = from; + while(idx > 0) { + idx --; + if(lookup(n)->array[idx].node.data) { + udb_ptr_set_rptr(n, udb, &lookup(n)->array[idx].node); + udb_radnode_last_in_subtree_incl_self(udb, n); + if(!udb_ptr_is_null(n)) + return; + } + } + udb_ptr_zero(n, udb); +} + +/** return self or a previous element */ +static int udb_ret_self_or_prev(udb_base* udb, udb_ptr* n, udb_ptr* result) +{ + if(RADNODE(n)->elem.data) { + udb_ptr_set_ptr(result, udb, n); + } else { + udb_ptr_set_ptr(result, udb, n); + udb_radix_prev(udb, result); + } + udb_ptr_unlink(n, udb); + return 0; +} + + +int udb_radix_find_less_equal(udb_base* udb, udb_ptr* rt, uint8_t* k, + udb_radstrlen_t len, udb_ptr* result) +{ + udb_ptr n; + udb_radstrlen_t pos = 0; + uint8_t byte; + int r; + /* set result to NULL */ + udb_ptr_init(result, udb); + if(RADTREE(rt)->count == 0) { + /* empty tree */ + return 0; + } + udb_ptr_new(&n, udb, &RADTREE(rt)->root); + while(pos < len) { + byte = k[pos]; + if(byte < RADNODE(&n)->offset) { + /* so the previous is the element itself */ + /* or something before this element */ + return udb_ret_self_or_prev(udb, &n, result); + } + byte -= RADNODE(&n)->offset; + if(byte >= lookup(&n)->len) { + /* so, the previous is the last of array, or itself */ + /* or something before this element */ + udb_ptr_set_ptr(result, udb, &n); + udb_radnode_last_in_subtree_incl_self(udb, result); + if(udb_ptr_is_null(result)) { + udb_ptr_set_ptr(result, udb, &n); + udb_radix_prev(udb, result); + } + goto done_fail; + } + pos++; + if(!lookup(&n)->array[byte].node.data) { + /* no match */ + /* Find an entry in arrays from byte-1 to 0 */ + udb_ptr_set_ptr(result, udb, &n); + udb_radnode_find_prev_from_idx(udb, result, byte); + if(!udb_ptr_is_null(result)) + goto done_fail; + /* this entry or something before it */ + udb_ptr_zero(result, udb); + return udb_ret_self_or_prev(udb, &n, result); + } + if(lookup_len(&n, byte) != 0) { + /* must match additional string */ + if(pos+lookup_len(&n, byte) > len) { + /* the additional string is longer than key*/ + if( (r=memcmp(&k[pos], lookup_string(&n, byte), + len-pos)) <= 0) { + /* and the key is before this node */ + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radix_prev(udb, result); + } else { + /* the key is after the additional + * string, thus everything in that + * subtree is smaller. */ + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radnode_last_in_subtree_incl_self(udb, result); + /* 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(udb_ptr_is_null(result)) { + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radix_prev(udb, result); + } + } + goto done_fail; /* no match */ + } + if( (r=memcmp(&k[pos], lookup_string(&n, byte), + lookup_len(&n, byte))) < 0) { + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radix_prev(udb, result); + goto done_fail; /* no match */ + } else if(r > 0) { + /* the key is larger than the additional + * string, thus everything in that subtree + * is smaller */ + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radnode_last_in_subtree_incl_self(udb, result); + /* if we have an inefficient tree */ + if(udb_ptr_is_null(result)) { + udb_ptr_set_rptr(result, udb, + &lookup(&n)->array[byte].node); + udb_radix_prev(udb, result); + } + goto done_fail; /* no match */ + } + pos += lookup_len(&n, byte); + } + udb_ptr_set_rptr(&n, udb, &lookup(&n)->array[byte].node); + } + if(RADNODE(&n)->elem.data) { + /* exact match */ + udb_ptr_set_ptr(result, udb, &n); + udb_ptr_unlink(&n, udb); + return 1; + } + /* there is a node which is an exact match, but it has no element */ + udb_ptr_set_ptr(result, udb, &n); + udb_radix_prev(udb, result); +done_fail: + udb_ptr_unlink(&n, udb); + return 0; +} + +void udb_radix_first(udb_base* udb, udb_ptr* rt, udb_ptr* p) +{ + udb_ptr_init(p, udb); + if(!rt || udb_ptr_is_null(rt) || RADTREE(rt)->count == 0) + return; + udb_ptr_set_rptr(p, udb, &RADTREE(rt)->root); + if(RADNODE(p)->elem.data) + return; + udb_radix_next(udb, p); +} + +void udb_radix_last(udb_base* udb, udb_ptr* rt, udb_ptr* p) +{ + udb_ptr_init(p, udb); + if(!rt || udb_ptr_is_null(rt) || RADTREE(rt)->count == 0) + return; + udb_ptr_set_rptr(p, udb, &RADTREE(rt)->root); + udb_radnode_last_in_subtree_incl_self(udb, p); +} + +void udb_radix_next(udb_base* udb, udb_ptr* n) +{ + udb_ptr s; + udb_ptr_init(&s, udb); + if(lookup(n)->len) { + /* go down */ + udb_ptr_set_ptr(&s, udb, n); + udb_radnode_first_in_subtree(udb, &s); + if(!udb_ptr_is_null(&s)) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + } + /* go up - the parent->elem is not useful, because it is before us */ + while(RADNODE(n)->parent.data) { + unsigned idx = RADNODE(n)->pidx; + udb_ptr_set_rptr(n, udb, &RADNODE(n)->parent); + idx++; + for(; idx < lookup(n)->len; idx++) { + /* go down the next branch */ + if(lookup(n)->array[idx].node.data) { + udb_ptr_set_rptr(&s, udb, + &lookup(n)->array[idx].node); + /* node itself */ + if(RADNODE(&s)->elem.data) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + /* or subtree */ + udb_radnode_first_in_subtree(udb, &s); + if(!udb_ptr_is_null(&s)) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + } + } + } + udb_ptr_unlink(&s, udb); + udb_ptr_zero(n, udb); +} + +void udb_radix_prev(udb_base* udb, udb_ptr* n) +{ + /* must go up, since all array nodes are after this node */ + while(RADNODE(n)->parent.data) { + uint8_t idx = RADNODE(n)->pidx; + udb_ptr s; + udb_ptr_set_rptr(n, udb, &RADNODE(n)->parent); + assert(lookup(n)->len > 0); /* since we are a child */ + /* see if there are elements in previous branches there */ + udb_ptr_init(&s, udb); + udb_ptr_set_ptr(&s, udb, n); + udb_radnode_find_prev_from_idx(udb, &s, idx); + if(!udb_ptr_is_null(&s)) { + udb_ptr_set_ptr(n, udb, &s); + udb_ptr_unlink(&s, udb); + return; + } + udb_ptr_unlink(&s, udb); + /* the current node is before the array */ + if(RADNODE(n)->elem.data) + return; + } + udb_ptr_zero(n, udb); +} + +udb_void udb_radname_insert(udb_base* udb, udb_ptr* rt, const uint8_t* dname, + size_t dlen, udb_ptr* elem, udb_ptr* result) +{ + uint8_t k[300]; + radstrlen_t klen = (radstrlen_t)sizeof(k); + radname_d2r(k, &klen, dname, dlen); + return udb_radix_insert(udb, rt, k, klen, elem, result); +} + +int udb_radname_search(udb_base* udb, udb_ptr* rt, const uint8_t* dname, + size_t dlen, udb_ptr* result) +{ + udb_void r; + uint8_t k[300]; + radstrlen_t klen = (radstrlen_t)sizeof(k); + radname_d2r(k, &klen, dname, dlen); + r = udb_radix_search(rt, k, klen); + udb_ptr_init(result, udb); + udb_ptr_set(result, udb, r); + return (r != 0); +} + +void udb_radix_tree_walk_chunk(void* base, void* d, uint64_t s, + udb_walk_relptr_cb* cb, void* arg) +{ + struct udb_radtree_d* p = (struct udb_radtree_d*)d; + assert(s >= sizeof(struct udb_radtree_d)); + (void)s; + (*cb)(base, &p->root, arg); +} + +void udb_radix_node_walk_chunk(void* base, void* d, uint64_t s, + udb_walk_relptr_cb* cb, void* arg) +{ + struct udb_radnode_d* p = (struct udb_radnode_d*)d; + assert(s >= sizeof(struct udb_radnode_d)); + (void)s; + (*cb)(base, &p->elem, arg); + (*cb)(base, &p->parent, arg); + (*cb)(base, &p->lookup, arg); +} + +void udb_radix_array_walk_chunk(void* base, void* d, uint64_t s, + udb_walk_relptr_cb* cb, void* arg) +{ + struct udb_radarray_d* p = (struct udb_radarray_d*)d; + unsigned i; + assert(s >= sizeof(struct udb_radarray_d)+ + p->capacity*(sizeof(struct udb_radsel_d)+p->str_cap)); + (void)s; + for(i=0; i<p->len; i++) { + (*cb)(base, &p->array[i].node, arg); + } +} |