2009-07-07 16:45:47 +00:00
|
|
|
|
|
|
|
#include <lighttpd/radix.h>
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* internal data is saved in "host"-order; search from high to low bit */
|
|
|
|
typedef guint32 rdxBase;
|
|
|
|
#define HTON_RDX(x) ((rdxBase) (htonl(x)))
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
#define RDXBITS (sizeof(rdxBase)*8)
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* 1^(width) 0^(RDXBITS-width): "1..10..0" */
|
2010-09-20 20:53:17 +00:00
|
|
|
#define RDX_MASK(width) ( width ? ~( (((rdxBase)1) << (RDXBITS - width)) - 1 ) : 0 )
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
#define INPUT_SIZE(bits) ( bits ? (bits+RDXBITS-1) / RDXBITS : 1 )
|
|
|
|
#define INPUT_CHARS(bits) ( (bits+7) / 8 )
|
|
|
|
|
|
|
|
typedef struct liRadixNode liRadixNode;
|
|
|
|
struct liRadixNode{
|
|
|
|
rdxBase key;
|
|
|
|
guint32 width;
|
|
|
|
gpointer data;
|
2010-09-20 20:53:17 +00:00
|
|
|
liRadixNode *right; /* "1" bit */
|
|
|
|
liRadixNode *left; /* "0" bit */
|
2009-12-14 13:29:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct liRadixTree {
|
|
|
|
liRadixNode *zero;
|
|
|
|
};
|
|
|
|
|
2010-08-23 14:53:03 +00:00
|
|
|
liRadixTree* li_radixtree_new(void) {
|
2009-12-14 13:29:59 +00:00
|
|
|
liRadixTree *tree;
|
|
|
|
|
|
|
|
tree = g_slice_new0(liRadixTree);
|
2009-05-29 13:03:19 +00:00
|
|
|
|
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* node != NULL */
|
|
|
|
static void li_radixtree_free_node(liRadixNode *node, GFunc free_func, gpointer free_userdata) {
|
|
|
|
if (node->right) {
|
|
|
|
li_radixtree_free_node(node->right, free_func, free_userdata);
|
|
|
|
}
|
|
|
|
if (node->left) {
|
|
|
|
li_radixtree_free_node(node->left, free_func, free_userdata);
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (free_func && node->data) free_func(node->data, free_userdata);
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
g_slice_free(liRadixNode, node);
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
void li_radixtree_free(liRadixTree *tree, GFunc free_func, gpointer free_userdata) {
|
|
|
|
if (tree->zero) li_radixtree_free_node(tree->zero, free_func, free_userdata);
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
g_slice_free(liRadixTree, tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rdx_get_input(rdxBase *dest, const void *key, guint32 bits) {
|
|
|
|
guint32 entries = INPUT_SIZE(bits), chars = INPUT_CHARS(bits), padding = entries*sizeof(rdxBase) - chars, i;
|
|
|
|
|
|
|
|
memcpy(dest, key, chars);
|
|
|
|
memset(((char*)dest) + chars, 0, padding);
|
|
|
|
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
|
|
dest[i] = HTON_RDX(dest[i]);
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
2009-12-14 13:29:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gpointer li_radixtree_insert(liRadixTree *tree, const void *key, guint32 bits, gpointer data) {
|
|
|
|
liRadixNode *node, **nodeloc;
|
|
|
|
rdxBase input[INPUT_SIZE(bits)], current;
|
|
|
|
guint32 pos = 0;
|
|
|
|
rdx_get_input(input, key, bits);
|
|
|
|
|
|
|
|
if (!data) return NULL;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
pos = 0;
|
|
|
|
current = input[0];
|
|
|
|
|
|
|
|
nodeloc = &tree->zero;
|
|
|
|
|
|
|
|
while (NULL != (node = *nodeloc)) {
|
|
|
|
rdxBase mask = RDX_MASK(node->width);
|
|
|
|
|
|
|
|
if (node->width > bits || (current & mask) != node->key) { /* prefix longer than key or key doesn't match */
|
|
|
|
/* split node */
|
|
|
|
liRadixNode *newnode;
|
|
|
|
guint32 width = (node->width > bits) ? bits : node->width;
|
|
|
|
mask = RDX_MASK(width);
|
|
|
|
while ((current & mask) != (node->key & mask)) {
|
|
|
|
width--;
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
|
|
|
newnode = g_slice_new0(liRadixNode);
|
|
|
|
newnode->width = width;
|
|
|
|
newnode->key = current & mask;
|
|
|
|
if (node->key & (1 << (RDXBITS-width-1))) { /* current may not have a "next" bit */
|
|
|
|
newnode->right = node;
|
|
|
|
*nodeloc = newnode;
|
|
|
|
nodeloc = &newnode->left;
|
2009-05-29 13:03:19 +00:00
|
|
|
} else {
|
2009-12-14 13:29:59 +00:00
|
|
|
newnode->left = node;
|
|
|
|
*nodeloc = newnode;
|
|
|
|
nodeloc = &newnode->right;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
2009-12-14 13:29:59 +00:00
|
|
|
if (width == bits) {
|
|
|
|
newnode->data = data;
|
|
|
|
return NULL;
|
2009-05-29 13:03:19 +00:00
|
|
|
} else {
|
2009-12-14 13:29:59 +00:00
|
|
|
/* NULL == *nodeloc */
|
|
|
|
break;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
2009-12-14 13:29:59 +00:00
|
|
|
}
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (node->width == bits) { /* exact match */
|
|
|
|
gpointer olddata = node->data;
|
2009-05-29 13:03:19 +00:00
|
|
|
node->data = data;
|
2009-12-14 13:29:59 +00:00
|
|
|
return olddata;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (mask & 0x1) {
|
|
|
|
/* next "layer" */
|
|
|
|
current = input[++pos];
|
|
|
|
bits -= RDXBITS;
|
|
|
|
nodeloc = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
|
|
|
} else {
|
|
|
|
nodeloc = (current & (1 << (RDXBITS-node->width-1))) ? &node->right : &node->left;
|
|
|
|
}
|
|
|
|
}
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
while (bits > RDXBITS) {
|
|
|
|
node = g_slice_new0(liRadixNode);
|
|
|
|
node->width = RDXBITS;
|
|
|
|
node->key = current;
|
|
|
|
*nodeloc = node;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* next "layer" */
|
|
|
|
current = input[++pos];
|
|
|
|
bits -= RDXBITS;
|
|
|
|
nodeloc = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
|
|
|
}
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
node = g_slice_new0(liRadixNode);
|
|
|
|
node->width = bits;
|
|
|
|
node->key = current & RDX_MASK(bits);
|
|
|
|
node->data = data;
|
|
|
|
*nodeloc = node;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
return NULL;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* *nodeptr == node is the only pointer to node from the parent! */
|
|
|
|
static void node_compact(liRadixNode **nodeptr, liRadixNode *node) {
|
|
|
|
liRadixNode *child;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* can't remove nodes with data: */
|
|
|
|
if (NULL != node->data) return;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (NULL == node->left && NULL == node->right) {
|
|
|
|
/* delete node */
|
|
|
|
*nodeptr = NULL;
|
|
|
|
g_slice_free(liRadixNode, node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* else: at least one child */
|
|
|
|
|
|
|
|
/* childs in the same layer? */
|
|
|
|
if (node->width != RDXBITS) {
|
|
|
|
/* exactly one child? (we have at least one!) */
|
|
|
|
if (NULL == node->left) {
|
|
|
|
child = node->right;
|
|
|
|
} else if (NULL == node->right) {
|
|
|
|
child = node->left;
|
2009-05-29 13:03:19 +00:00
|
|
|
} else {
|
2009-12-14 13:29:59 +00:00
|
|
|
return; /* two childs */
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
/* replace our own node with child */
|
|
|
|
*nodeptr = child;
|
|
|
|
g_slice_free(liRadixNode, node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *nodeptr == node is the only pointer to the node from the parent! */
|
|
|
|
static gpointer radixtree_remove(liRadixNode **nodeptr, rdxBase *input, guint32 bits) {
|
|
|
|
liRadixNode **nextnode;
|
|
|
|
gpointer data;
|
|
|
|
rdxBase current, mask;
|
|
|
|
liRadixNode *node = *nodeptr;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (!node) return NULL;
|
|
|
|
|
|
|
|
current = *input;
|
|
|
|
mask = RDX_MASK(node->width);
|
|
|
|
|
|
|
|
if (node->width > bits) return NULL; /* prefix longer than key */
|
|
|
|
|
|
|
|
if ((current & mask) != node->key) return NULL; /* doesn't match */
|
|
|
|
|
|
|
|
if (node->width == bits) { /* exact match */
|
|
|
|
data = node->data;
|
|
|
|
node->data = NULL;
|
|
|
|
node_compact(nodeptr, node);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & 0x1) {
|
|
|
|
/* next "layer" */
|
|
|
|
input++;
|
|
|
|
bits -= RDXBITS;
|
|
|
|
nextnode = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
|
|
|
} else {
|
|
|
|
nextnode = (current & (1 << (RDXBITS-node->width-1))) ? &node->right : &node->left;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
data = radixtree_remove(nextnode, input, bits);
|
|
|
|
|
|
|
|
if (data == NULL) return NULL; /* nothing deleted */
|
|
|
|
|
|
|
|
node_compact(nodeptr, node);
|
|
|
|
|
|
|
|
return data;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
gpointer li_radixtree_remove(liRadixTree *tree, const void *key, guint32 bits) {
|
|
|
|
rdxBase input[INPUT_SIZE(bits)];
|
|
|
|
gpointer data;
|
|
|
|
rdx_get_input(input, key, bits);
|
|
|
|
|
|
|
|
data = radixtree_remove(&tree->zero, input, bits);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
gpointer li_radixtree_lookup(liRadixTree *tree, const void *key, guint32 bits) { /* longest matching prefix */
|
|
|
|
liRadixNode *node;
|
|
|
|
rdxBase input[INPUT_SIZE(bits)], current;
|
|
|
|
guint32 pos = 0;
|
|
|
|
gpointer data = NULL;
|
|
|
|
|
|
|
|
rdx_get_input(input, key, bits);
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
current = input[0];
|
|
|
|
|
|
|
|
node = tree->zero;
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-07-16 10:57:11 +00:00
|
|
|
while (node) {
|
2009-12-14 13:29:59 +00:00
|
|
|
rdxBase mask = RDX_MASK(node->width);
|
|
|
|
|
|
|
|
if (node->width > bits) break; /* prefix longer than key */
|
|
|
|
|
|
|
|
if ((current & mask) != node->key) break; /* doesn't match */
|
|
|
|
|
|
|
|
if (node->data) data = node->data; /* longest matching prefix */
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (node->width == bits) break; /* "end of key" */
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (mask & 0x1) {
|
|
|
|
/* next "layer" */
|
|
|
|
current = input[++pos];
|
|
|
|
bits -= RDXBITS;
|
|
|
|
node = (current & (1 << (RDXBITS-1))) ? node->right : node->left;
|
|
|
|
} else {
|
|
|
|
node = (current & (1 << (RDXBITS-node->width-1))) ? node->right : node->left;
|
|
|
|
}
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
return data;
|
2009-05-29 13:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
gpointer li_radixtree_lookup_exact(liRadixTree *tree, const void *key, guint32 bits) {
|
|
|
|
liRadixNode *node;
|
|
|
|
rdxBase input[INPUT_SIZE(bits)], current;
|
|
|
|
guint32 pos = 0;
|
|
|
|
|
|
|
|
rdx_get_input(input, key, bits);
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
current = input[0];
|
|
|
|
|
|
|
|
node = tree->zero;
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
rdxBase mask = RDX_MASK(node->width);
|
|
|
|
|
|
|
|
if (node->width > bits) break; /* prefix longer than key */
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if ((current & mask) != node->key) break; /* doesn't match */
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
if (node->width == bits) return node->data; /* exact match */
|
|
|
|
|
|
|
|
if (mask & 0x1) {
|
|
|
|
/* next "layer" */
|
|
|
|
current = input[++pos];
|
|
|
|
bits -= RDXBITS;
|
|
|
|
node = (current & (1 << (RDXBITS-1))) ? node->right : node->left;
|
|
|
|
} else {
|
|
|
|
node = (current & (1 << (RDXBITS-node->width-1))) ? node->right : node->left;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* node != NULL */
|
|
|
|
static void radixtree_foreach(liRadixNode *node, GFunc func, gpointer userdata) {
|
|
|
|
if (node->data) {
|
|
|
|
func(node->data, userdata);
|
|
|
|
}
|
|
|
|
if (node->right) radixtree_foreach(node->right, func, userdata);
|
|
|
|
if (node->left) radixtree_foreach(node->left, func, userdata);
|
|
|
|
}
|
2009-05-29 13:03:19 +00:00
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
void li_radixtree_foreach(liRadixTree *tree, GFunc func, gpointer userdata) {
|
|
|
|
if (tree->zero) radixtree_foreach(tree->zero, func, userdata);
|
2009-07-16 20:17:14 +00:00
|
|
|
}
|