代码拉取完成,页面将自动刷新
/*
* 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;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。