/* * radtree -- generic radix tree for binary strings. * * Copyright (c) 2010, NLnet Labs. See LICENSE for license. */ #include "config.h" #include #include #include #include #include #include "radtree.h" #include "util.h" #include "region-allocator.h" #include #include 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; ilen; 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; idxlen; 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_t len, struct radnode** result, radstrlen_t* respos) { struct radnode* n = rt->root; radstrlen_t 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; } 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(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_t pos, radstrlen_t 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_t plen, uint8_t* x, radstrlen_t 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_t bstr_common(uint8_t* x, radstrlen_t xlen, uint8_t* y, radstrlen_t ylen) { unsigned i, max = ((xlenstr, r->len)) { uint8_t* split_str=NULL, *dupstr=NULL; 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 != 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_t 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_t 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_t len, void* elem) { struct radnode* n; radstrlen_t 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) 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->offsetlen); 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; ilen; 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_t 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 */ 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 */ 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(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; idxlen; 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_t len) { struct radnode* n = rt->root; radstrlen_t 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_t len, struct radnode** result) { struct radnode* n = rt->root; radstrlen_t 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->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) { /* 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= 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_t 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_t len = (radstrlen_t)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_t 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; iarray[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_t 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; iarray[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; }