3 Star 0 Fork 1

Gitee 极速下载/NSD0

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/NLnetLabs/nsd
克隆/下载
radtree.c 37.51 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
/*
* 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;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C/C++
1
https://gitee.com/mirrors/NSD0.git
git@gitee.com:mirrors/NSD0.git
mirrors
NSD0
NSD0
master

搜索帮助

371d5123 14472233 46e8bd33 14472233