freebsd-skq/contrib/ldns/radix.c
2014-05-15 03:30:03 +00:00

1591 lines
36 KiB
C

/*
* radix.c -- generic radix tree
*
* Taken from NSD4, modified for ldns
*
* Copyright (c) 2012, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Implementation of a radix tree.
*/
#include <ldns/config.h>
#include <ldns/radix.h>
#include <ldns/util.h>
#include <stdlib.h>
/** Helper functions */
static ldns_radix_node_t* ldns_radix_new_node(void* data, uint8_t* key,
radix_strlen_t len);
static int ldns_radix_find_prefix(ldns_radix_t* tree, uint8_t* key,
radix_strlen_t len, ldns_radix_node_t** result, radix_strlen_t* pos);
static int ldns_radix_array_space(ldns_radix_node_t* node, uint8_t byte);
static int ldns_radix_array_grow(ldns_radix_node_t* node, unsigned need);
static int ldns_radix_str_create(ldns_radix_array_t* array, uint8_t* key,
radix_strlen_t pos, radix_strlen_t len);
static int ldns_radix_prefix_remainder(radix_strlen_t prefix_len,
uint8_t* longer_str, radix_strlen_t longer_len, uint8_t** split_str,
radix_strlen_t* split_len);
static int ldns_radix_array_split(ldns_radix_array_t* array, uint8_t* key,
radix_strlen_t pos, radix_strlen_t len, ldns_radix_node_t* add);
static int ldns_radix_str_is_prefix(uint8_t* str1, radix_strlen_t len1,
uint8_t* str2, radix_strlen_t len2);
static radix_strlen_t ldns_radix_str_common(uint8_t* str1, radix_strlen_t len1,
uint8_t* str2, radix_strlen_t len2);
static ldns_radix_node_t* ldns_radix_next_in_subtree(ldns_radix_node_t* node);
static ldns_radix_node_t* ldns_radix_prev_from_index(ldns_radix_node_t* node,
uint8_t index);
static ldns_radix_node_t* ldns_radix_last_in_subtree_incl_self(
ldns_radix_node_t* node);
static ldns_radix_node_t* ldns_radix_last_in_subtree(ldns_radix_node_t* node);
static void ldns_radix_del_fix(ldns_radix_t* tree, ldns_radix_node_t* node);
static void ldns_radix_cleanup_onechild(ldns_radix_node_t* node);
static void ldns_radix_cleanup_leaf(ldns_radix_node_t* node);
static void ldns_radix_node_free(ldns_radix_node_t* node, void* arg);
static void ldns_radix_node_array_free(ldns_radix_node_t* node);
static void ldns_radix_node_array_free_front(ldns_radix_node_t* node);
static void ldns_radix_node_array_free_end(ldns_radix_node_t* node);
static void ldns_radix_array_reduce(ldns_radix_node_t* node);
static void ldns_radix_self_or_prev(ldns_radix_node_t* node,
ldns_radix_node_t** result);
/**
* Create a new radix node.
*
*/
static ldns_radix_node_t*
ldns_radix_new_node(void* data, uint8_t* key, radix_strlen_t len)
{
ldns_radix_node_t* node = LDNS_MALLOC(ldns_radix_node_t);
if (!node) {
return NULL;
}
node->data = data;
node->key = key;
node->klen = len;
node->parent = NULL;
node->parent_index = 0;
node->len = 0;
node->offset = 0;
node->capacity = 0;
node->array = NULL;
return node;
}
/**
* Create a new radix tree.
*
*/
ldns_radix_t *
ldns_radix_create(void)
{
ldns_radix_t* tree;
/** Allocate memory for it */
tree = (ldns_radix_t *) LDNS_MALLOC(ldns_radix_t);
if (!tree) {
return NULL;
}
/** Initialize it */
ldns_radix_init(tree);
return tree;
}
/**
* Initialize radix tree.
*
*/
void
ldns_radix_init(ldns_radix_t* tree)
{
/** Initialize it */
if (tree) {
tree->root = NULL;
tree->count = 0;
}
return;
}
/**
* Free radix tree.
*
*/
void
ldns_radix_free(ldns_radix_t* tree)
{
if (tree) {
if (tree->root) {
ldns_radix_traverse_postorder(tree->root,
ldns_radix_node_free, NULL);
}
LDNS_FREE(tree);
}
return;
}
/**
* Insert data into the tree.
*
*/
ldns_status
ldns_radix_insert(ldns_radix_t* tree, uint8_t* key, radix_strlen_t len,
void* data)
{
radix_strlen_t pos = 0;
ldns_radix_node_t* add = NULL;
ldns_radix_node_t* prefix = NULL;
if (!tree || !key || !data) {
return LDNS_STATUS_NULL;
}
add = ldns_radix_new_node(data, key, len);
if (!add) {
return LDNS_STATUS_MEM_ERR;
}
/** Search the trie until we can make no further process. */
if (!ldns_radix_find_prefix(tree, key, len, &prefix, &pos)) {
/** No prefix found */
assert(tree->root == NULL);
if (len == 0) {
/**
* Example 1: The root:
* | [0]
**/
tree->root = add;
} else {
/** Example 2: 'dns':
* | [0]
* --| [d+ns] dns
**/
prefix = ldns_radix_new_node(NULL, (uint8_t*)"", 0);
if (!prefix) {
LDNS_FREE(add);
return LDNS_STATUS_MEM_ERR;
}
/** Find some space in the array for the first byte */
if (!ldns_radix_array_space(prefix, key[0])) {
LDNS_FREE(add);
LDNS_FREE(prefix->array);
LDNS_FREE(prefix);
return LDNS_STATUS_MEM_ERR;
}
/** Set relational pointers */
add->parent = prefix;
add->parent_index = 0;
prefix->array[0].edge = add;
if (len > 1) {
/** Store the remainder of the prefix */
if (!ldns_radix_prefix_remainder(1, key,
len, &prefix->array[0].str,
&prefix->array[0].len)) {
LDNS_FREE(add);
LDNS_FREE(prefix->array);
LDNS_FREE(prefix);
return LDNS_STATUS_MEM_ERR;
}
}
tree->root = prefix;
}
} else if (pos == len) {
/** Exact match found */
if (prefix->data) {
/* Element already exists */
LDNS_FREE(add);
return LDNS_STATUS_EXISTS_ERR;
}
prefix->data = data;
prefix->key = key;
prefix->klen = len; /* redundant */
} else {
/** Prefix found */
uint8_t byte = key[pos];
assert(pos < len);
if (byte < prefix->offset ||
(byte - prefix->offset) >= prefix->len) {
/** Find some space in the array for the byte. */
/**
* Example 3: 'ldns'
* | [0]
* --| [d+ns] dns
* --| [l+dns] ldns
**/
if (!ldns_radix_array_space(prefix, byte)) {
LDNS_FREE(add);
return LDNS_STATUS_MEM_ERR;
}
assert(byte >= prefix->offset);
assert((byte - prefix->offset) <= prefix->len);
byte -= prefix->offset;
if (pos+1 < len) {
/** Create remainder of the string. */
if (!ldns_radix_str_create(
&prefix->array[byte], key, pos+1,
len)) {
LDNS_FREE(add);
return LDNS_STATUS_MEM_ERR;
}
}
/** Add new node. */
add->parent = prefix;
add->parent_index = byte;
prefix->array[byte].edge = add;
} else if (prefix->array[byte-prefix->offset].edge == NULL) {
/** Use existing element. */
/**
* Example 4: 'edns'
* | [0]
* --| [d+ns] dns
* --| [e+dns] edns
* --| [l+dns] ldns
**/
byte -= prefix->offset;
if (pos+1 < len) {
/** Create remainder of the string. */
if (!ldns_radix_str_create(
&prefix->array[byte], key, pos+1,
len)) {
LDNS_FREE(add);
return LDNS_STATUS_MEM_ERR;
}
}
/** Add new node. */
add->parent = prefix;
add->parent_index = byte;
prefix->array[byte].edge = add;
} else {
/**
* Use existing element, but it has a shared prefix,
* we need a split.
*/
if (!ldns_radix_array_split(&prefix->array[byte-(prefix->offset)],
key, pos+1, len, add)) {
LDNS_FREE(add);
return LDNS_STATUS_MEM_ERR;
}
}
}
tree->count ++;
return LDNS_STATUS_OK;
}
/**
* Delete data from the tree.
*
*/
void* ldns_radix_delete(ldns_radix_t* tree, uint8_t* key, radix_strlen_t len)
{
ldns_radix_node_t* del = ldns_radix_search(tree, key, len);
void* data = NULL;
if (del) {
tree->count--;
data = del->data;
del->data = NULL;
ldns_radix_del_fix(tree, del);
return data;
}
return NULL;
}
/**
* Search data in the tree.
*
*/
ldns_radix_node_t*
ldns_radix_search(ldns_radix_t* tree, uint8_t* key, radix_strlen_t len)
{
ldns_radix_node_t* node = NULL;
radix_strlen_t pos = 0;
uint8_t byte = 0;
if (!tree || !key) {
return NULL;
}
node = tree->root;
while (node) {
if (pos == len) {
return node->data?node:NULL;
}
byte = key[pos];
if (byte < node->offset) {
return NULL;
}
byte -= node->offset;
if (byte >= node->len) {
return NULL;
}
pos++;
if (node->array[byte].len > 0) {
/** Must match additional string. */
if (pos + node->array[byte].len > len) {
return NULL;
}
if (memcmp(&key[pos], node->array[byte].str,
node->array[byte].len) != 0) {
return NULL;
}
pos += node->array[byte].len;
}
node = node->array[byte].edge;
}
return NULL;
}
/**
* Search data in the tree, and if not found, find the closest smaller
* element in the tree.
*
*/
int
ldns_radix_find_less_equal(ldns_radix_t* tree, uint8_t* key,
radix_strlen_t len, ldns_radix_node_t** result)
{
ldns_radix_node_t* node = NULL;
radix_strlen_t pos = 0;
uint8_t byte;
int memcmp_res = 0;
if (!tree || !tree->root || !key) {
*result = NULL;
return 0;
}
node = tree->root;
while (pos < len) {
byte = key[pos];
if (byte < node->offset) {
/**
* No exact match. The lesser is in this or the
* previous node.
*/
ldns_radix_self_or_prev(node, result);
return 0;
}
byte -= node->offset;
if (byte >= node->len) {
/**
* No exact match. The lesser is in this node or the
* last of this array, or something before this node.
*/
*result = ldns_radix_last_in_subtree_incl_self(node);
if (*result == NULL) {
*result = ldns_radix_prev(node);
}
return 0;
}
pos++;
if (!node->array[byte].edge) {
/**
* No exact match. Find the previous in the array
* from this index.
*/
*result = ldns_radix_prev_from_index(node, byte);
if (*result == NULL) {
ldns_radix_self_or_prev(node, result);
}
return 0;
}
if (node->array[byte].len != 0) {
/** Must match additional string. */
if (pos + node->array[byte].len > len) {
/** Additional string is longer than key. */
if (memcmp(&key[pos], node->array[byte].str,
len-pos) <= 0) {
/** Key is before this node. */
*result = ldns_radix_prev(
node->array[byte].edge);
} else {
/** Key is after additional string. */
*result = ldns_radix_last_in_subtree_incl_self(node->array[byte].edge);
if (*result == NULL) {
*result = ldns_radix_prev(node->array[byte].edge);
}
}
return 0;
}
memcmp_res = memcmp(&key[pos], node->array[byte].str,
node->array[byte].len);
if (memcmp_res < 0) {
*result = ldns_radix_prev(
node->array[byte].edge);
return 0;
} else if (memcmp_res > 0) {
*result = ldns_radix_last_in_subtree_incl_self(node->array[byte].edge);
if (*result == NULL) {
*result = ldns_radix_prev(node->array[byte].edge);
}
return 0;
}
pos += node->array[byte].len;
}
node = node->array[byte].edge;
}
if (node->data) {
/** Exact match. */
*result = node;
return 1;
}
/** There is a node which is an exact match, but has no element. */
*result = ldns_radix_prev(node);
return 0;
}
/**
* Get the first element in the tree.
*
*/
ldns_radix_node_t*
ldns_radix_first(ldns_radix_t* tree)
{
ldns_radix_node_t* first = NULL;
if (!tree || !tree->root) {
return NULL;
}
first = tree->root;
if (first->data) {
return first;
}
return ldns_radix_next(first);
}
/**
* Get the last element in the tree.
*
*/
ldns_radix_node_t*
ldns_radix_last(ldns_radix_t* tree)
{
if (!tree || !tree->root) {
return NULL;
}
return ldns_radix_last_in_subtree_incl_self(tree->root);
}
/**
* Next element.
*
*/
ldns_radix_node_t*
ldns_radix_next(ldns_radix_node_t* node)
{
if (!node) {
return NULL;
}
if (node->len) {
/** Go down: most-left child is the next. */
ldns_radix_node_t* next = ldns_radix_next_in_subtree(node);
if (next) {
return next;
}
}
/** No elements in subtree, get to parent and go down next branch. */
while (node->parent) {
uint8_t index = node->parent_index;
node = node->parent;
index++;
for (; index < node->len; index++) {
if (node->array[index].edge) {
ldns_radix_node_t* next;
/** Node itself. */
if (node->array[index].edge->data) {
return node->array[index].edge;
}
/** Dive into subtree. */
next = ldns_radix_next_in_subtree(node);
if (next) {
return next;
}
}
}
}
return NULL;
}
/**
* Previous element.
*
*/
ldns_radix_node_t*
ldns_radix_prev(ldns_radix_node_t* node)
{
if (!node) {
return NULL;
}
/** Get to parent and go down previous branch. */
while (node->parent) {
uint8_t index = node->parent_index;
ldns_radix_node_t* prev;
node = node->parent;
assert(node->len > 0);
prev = ldns_radix_prev_from_index(node, index);
if (prev) {
return prev;
}
if (node->data) {
return node;
}
}
return NULL;
}
/**
* Print node.
*
*/
static void
ldns_radix_node_print(FILE* fd, ldns_radix_node_t* node,
uint8_t i, uint8_t* str, radix_strlen_t len, unsigned d)
{
uint8_t j;
if (!node) {
return;
}
for (j = 0; j < d; j++) {
fprintf(fd, "--");
}
if (str) {
radix_strlen_t l;
fprintf(fd, "| [%u+", (unsigned) i);
for (l=0; l < len; l++) {
fprintf(fd, "%c", (char) str[l]);
}
fprintf(fd, "]%u", (unsigned) len);
} else {
fprintf(fd, "| [%u]", (unsigned) i);
}
if (node->data) {
fprintf(fd, " %s", (char*) node->data);
}
fprintf(fd, "\n");
for (j = 0; j < node->len; j++) {
if (node->array[j].edge) {
ldns_radix_node_print(fd, node->array[j].edge, j,
node->array[j].str, node->array[j].len, d+1);
}
}
return;
}
/**
* Print radix tree.
*
*/
void
ldns_radix_printf(FILE* fd, ldns_radix_t* tree)
{
if (!fd || !tree) {
return;
}
if (!tree->root) {
fprintf(fd, "; empty radix tree\n");
return;
}
ldns_radix_node_print(fd, tree->root, 0, NULL, 0, 0);
return;
}
/**
* Join two radix trees.
*
*/
ldns_status
ldns_radix_join(ldns_radix_t* tree1, ldns_radix_t* tree2)
{
ldns_radix_node_t* cur_node, *next_node;
ldns_status status;
if (!tree2 || !tree2->root) {
return LDNS_STATUS_OK;
}
/** Add all elements from tree2 into tree1. */
cur_node = ldns_radix_first(tree2);
while (cur_node) {
status = LDNS_STATUS_NO_DATA;
/** Insert current node into tree1 */
if (cur_node->data) {
status = ldns_radix_insert(tree1, cur_node->key,
cur_node->klen, cur_node->data);
/** Exist errors may occur */
if (status != LDNS_STATUS_OK &&
status != LDNS_STATUS_EXISTS_ERR) {
return status;
}
}
next_node = ldns_radix_next(cur_node);
if (status == LDNS_STATUS_OK) {
(void) ldns_radix_delete(tree2, cur_node->key,
cur_node->klen);
}
cur_node = next_node;
}
return LDNS_STATUS_OK;
}
/**
* Split a radix tree intwo.
*
*/
ldns_status
ldns_radix_split(ldns_radix_t* tree1, size_t num, ldns_radix_t** tree2)
{
size_t count = 0;
ldns_radix_node_t* cur_node;
ldns_status status = LDNS_STATUS_OK;
if (!tree1 || !tree1->root || num == 0) {
return LDNS_STATUS_OK;
}
if (!tree2) {
return LDNS_STATUS_NULL;
}
if (!*tree2) {
*tree2 = ldns_radix_create();
if (!*tree2) {
return LDNS_STATUS_MEM_ERR;
}
}
cur_node = ldns_radix_first(tree1);
while (count < num && cur_node) {
if (cur_node->data) {
/** Delete current node from tree1. */
uint8_t* cur_key = cur_node->key;
radix_strlen_t cur_len = cur_node->klen;
void* cur_data = ldns_radix_delete(tree1, cur_key,
cur_len);
/** Insert current node into tree2/ */
if (!cur_data) {
return LDNS_STATUS_NO_DATA;
}
status = ldns_radix_insert(*tree2, cur_key, cur_len,
cur_data);
if (status != LDNS_STATUS_OK &&
status != LDNS_STATUS_EXISTS_ERR) {
return status;
}
/*
if (status == LDNS_STATUS_OK) {
cur_node->key = NULL;
cur_node->klen = 0;
}
*/
/** Update count; get first element from tree1 again. */
count++;
cur_node = ldns_radix_first(tree1);
} else {
cur_node = ldns_radix_next(cur_node);
}
}
return LDNS_STATUS_OK;
}
/**
* Call function for all nodes in the tree, such that leaf nodes are
* called before parent nodes.
*
*/
void
ldns_radix_traverse_postorder(ldns_radix_node_t* node,
void (*func)(ldns_radix_node_t*, void*), void* arg)
{
uint8_t i;
if (!node) {
return;
}
for (i=0; i < node->len; i++) {
ldns_radix_traverse_postorder(node->array[i].edge,
func, arg);
}
/** Call user function */
(*func)(node, arg);
return;
}
/** Static helper functions */
/**
* Find a prefix of the key.
* @param tree: tree.
* @param key: key.
* @param len: length of key.
* @param result: the longest prefix, the entry itself if *pos==len,
* otherwise an array entry.
* @param pos: position in string where next unmatched byte is.
* If *pos==len, an exact match is found.
* If *pos== 0, a "" match was found.
* @return 0 (false) if no prefix found.
*
*/
static int
ldns_radix_find_prefix(ldns_radix_t* tree, uint8_t* key,
radix_strlen_t len, ldns_radix_node_t** result, radix_strlen_t* respos)
{
/** Start searching at the root node */
ldns_radix_node_t* n = tree->root;
radix_strlen_t pos = 0;
uint8_t byte;
*respos = 0;
*result = n;
if (!n) {
/** No root, no prefix found */
return 0;
}
/** For each node, look if we can make further progress */
while (n) {
if (pos == len) {
/** Exact match */
return 1;
}
byte = key[pos];
if (byte < n->offset) {
/** key < node */
return 1;
}
byte -= n->offset;
if (byte >= n->len) {
/** key > node */
return 1;
}
/** So far, the trie matches */
pos++;
if (n->array[byte].len != 0) {
/** Must match additional string */
if (pos + n->array[byte].len > len) {
return 1; /* no match at child node */
}
if (memcmp(&key[pos], n->array[byte].str,
n->array[byte].len) != 0) {
return 1; /* no match at child node */
}
pos += n->array[byte].len;
}
/** Continue searching prefix at this child node */
n = n->array[byte].edge;
if (!n) {
return 1;
}
/** Update the prefix node */
*respos = pos;
*result = n;
}
/** Done */
return 1;
}
/**
* Make space in the node's array for another byte.
* @param node: node.
* @param byte: byte.
* @return 1 if successful, 0 otherwise.
*
*/
static int
ldns_radix_array_space(ldns_radix_node_t* node, uint8_t byte)
{
/** Is there an array? */
if (!node->array) {
assert(node->capacity == 0);
/** No array, create new array */
node->array = LDNS_MALLOC(ldns_radix_array_t);
if (!node->array) {
return 0;
}
memset(&node->array[0], 0, sizeof(ldns_radix_array_t));
node->len = 1;
node->capacity = 1;
node->offset = byte;
return 1;
}
/** Array exist */
assert(node->array != NULL);
assert(node->capacity > 0);
if (node->len == 0) {
/** Unused array */
node->len = 1;
node->offset = byte;
} else if (byte < node->offset) {
/** Byte is below the offset */
uint8_t index;
uint16_t need = node->offset - byte;
/** Is there enough capacity? */
if (node->len + need > node->capacity) {
/** Not enough capacity, grow array */
if (!ldns_radix_array_grow(node,
(unsigned) (node->len + need))) {
return 0; /* failed to grow array */
}
}
/** Move items to the end */
memmove(&node->array[need], &node->array[0],
node->len*sizeof(ldns_radix_array_t));
/** Fix parent index */
for (index = 0; index < node->len; index++) {
if (node->array[index+need].edge) {
node->array[index+need].edge->parent_index =
index + need;
}
}
/** Zero the first */
memset(&node->array[0], 0, need*sizeof(ldns_radix_array_t));
node->len += need;
node->offset = byte;
} else if (byte - node->offset >= node->len) {
/** Byte does not fit in array */
uint16_t need = (byte - node->offset) - node->len + 1;
/** Is there enough capacity? */
if (node->len + need > node->capacity) {
/** Not enough capacity, grow array */
if (!ldns_radix_array_grow(node,
(unsigned) (node->len + need))) {
return 0; /* failed to grow array */
}
}
/** Zero the added items */
memset(&node->array[node->len], 0,
need*sizeof(ldns_radix_array_t));
node->len += need;
}
return 1;
}
/**
* Grow the array.
* @param node: node.
* @param need: number of elements the array at least need to grow.
* Can't be bigger than 256.
* @return: 0 if failed, 1 if was successful.
*
*/
static int
ldns_radix_array_grow(ldns_radix_node_t* node, unsigned need)
{
unsigned size = ((unsigned)node->capacity)*2;
ldns_radix_array_t* a = NULL;
if (need > size) {
size = need;
}
if (size > 256) {
size = 256;
}
a = LDNS_XMALLOC(ldns_radix_array_t, size);
if (!a) {
return 0;
}
assert(node->len <= node->capacity);
assert(node->capacity < size);
memcpy(&a[0], &node->array[0], node->len*sizeof(ldns_radix_array_t));
LDNS_FREE(node->array);
node->array = a;
node->capacity = size;
return 1;
}
/**
* Create a prefix in the array string.
* @param array: array.
* @param key: key.
* @param pos: start position in key.
* @param len: length of key.
* @return 0 if failed, 1 if was successful.
*
*/
static int
ldns_radix_str_create(ldns_radix_array_t* array, uint8_t* key,
radix_strlen_t pos, radix_strlen_t len)
{
array->str = LDNS_XMALLOC(uint8_t, (len-pos));
if (!array->str) {
return 0;
}
memmove(array->str, key+pos, len-pos);
array->len = (len-pos);
return 1;
}
/**
* Allocate remainder from prefixes for a split.
* @param prefixlen: length of prefix.
* @param longer_str: the longer string.
* @param longer_len: the longer string length.
* @param split_str: the split string.
* @param split_len: the split string length.
* @return 0 if failed, 1 if successful.
*
*/
static int
ldns_radix_prefix_remainder(radix_strlen_t prefix_len,
uint8_t* longer_str, radix_strlen_t longer_len,
uint8_t** split_str, radix_strlen_t* split_len)
{
*split_len = longer_len - prefix_len;
*split_str = LDNS_XMALLOC(uint8_t, (*split_len));
if (!*split_str) {
return 0;
}
memmove(*split_str, longer_str+prefix_len, longer_len-prefix_len);
return 1;
}
/**
* Create a split when two nodes have a shared prefix.
* @param array: array.
* @param key: key.
* @param pos: start position in key.
* @param len: length of the key.
* @param add: node to be added.
* @return 0 if failed, 1 if was successful.
*
*/
static int
ldns_radix_array_split(ldns_radix_array_t* array, uint8_t* key,
radix_strlen_t pos, radix_strlen_t len, ldns_radix_node_t* add)
{
uint8_t* str_to_add = key + pos;
radix_strlen_t strlen_to_add = len - pos;
if (ldns_radix_str_is_prefix(str_to_add, strlen_to_add,
array->str, array->len)) {
/** The string to add is a prefix of the existing string */
uint8_t* split_str = NULL, *dup_str = NULL;
radix_strlen_t split_len = 0;
/**
* Example 5: 'ld'
* | [0]
* --| [d+ns] dns
* --| [e+dns] edns
* --| [l+d] ld
* ----| [n+s] ldns
**/
assert(strlen_to_add < array->len);
/** Store the remainder in the split string */
if (array->len - strlen_to_add > 1) {
if (!ldns_radix_prefix_remainder(strlen_to_add+1,
array->str, array->len, &split_str,
&split_len)) {
return 0;
}
}
/** Duplicate the string to add */
if (strlen_to_add != 0) {
dup_str = LDNS_XMALLOC(uint8_t, strlen_to_add);
if (!dup_str) {
LDNS_FREE(split_str);
return 0;
}
memcpy(dup_str, str_to_add, strlen_to_add);
}
/** Make space in array for the new node */
if (!ldns_radix_array_space(add,
array->str[strlen_to_add])) {
LDNS_FREE(split_str);
LDNS_FREE(dup_str);
return 0;
}
/**
* The added node should go direct under the existing parent.
* The existing node should go under the added node.
*/
add->parent = array->edge->parent;
add->parent_index = array->edge->parent_index;
add->array[0].edge = array->edge;
add->array[0].str = split_str;
add->array[0].len = split_len;
array->edge->parent = add;
array->edge->parent_index = 0;
LDNS_FREE(array->str);
array->edge = add;
array->str = dup_str;
array->len = strlen_to_add;
} else if (ldns_radix_str_is_prefix(array->str, array->len,
str_to_add, strlen_to_add)) {
/** The existing string is a prefix of the string to add */
/**
* Example 6: 'dns-ng'
* | [0]
* --| [d+ns] dns
* ----| [-+ng] dns-ng
* --| [e+dns] edns
* --| [l+d] ld
* ----| [n+s] ldns
**/
uint8_t* split_str = NULL;
radix_strlen_t split_len = 0;
assert(array->len < strlen_to_add);
if (strlen_to_add - array->len > 1) {
if (!ldns_radix_prefix_remainder(array->len+1,
str_to_add, strlen_to_add, &split_str,
&split_len)) {
return 0;
}
}
/** Make space in array for the new node */
if (!ldns_radix_array_space(array->edge,
str_to_add[array->len])) {
LDNS_FREE(split_str);
return 0;
}
/**
* The added node should go direct under the existing node.
*/
add->parent = array->edge;
add->parent_index = str_to_add[array->len] -
array->edge->offset;
array->edge->array[add->parent_index].edge = add;
array->edge->array[add->parent_index].str = split_str;
array->edge->array[add->parent_index].len = split_len;
} else {
/** Create a new split node. */
/**
* Example 7: 'dndns'
* | [0]
* --| [d+n]
* ----| [d+ns] dndns
* ----| [s] dns
* ------| [-+ng] dns-ng
* --| [e+dns] edns
* --| [l+d] ld
* ----| [n+s] ldns
**/
ldns_radix_node_t* common = NULL;
uint8_t* common_str = NULL, *s1 = NULL, *s2 = NULL;
radix_strlen_t common_len = 0, l1 = 0, l2 = 0;
common_len = ldns_radix_str_common(array->str, array->len,
str_to_add, strlen_to_add);
assert(common_len < array->len);
assert(common_len < strlen_to_add);
/** Create the new common node. */
common = ldns_radix_new_node(NULL, (uint8_t*)"", 0);
if (!common) {
return 0;
}
if (array->len - common_len > 1) {
if (!ldns_radix_prefix_remainder(common_len+1,
array->str, array->len, &s1, &l1)) {
return 0;
}
}
if (strlen_to_add - common_len > 1) {
if (!ldns_radix_prefix_remainder(common_len+1,
str_to_add, strlen_to_add, &s2, &l2)) {
return 0;
}
}
/** Create the shared prefix. */
if (common_len > 0) {
common_str = LDNS_XMALLOC(uint8_t, common_len);
if (!common_str) {
LDNS_FREE(common);
LDNS_FREE(s1);
LDNS_FREE(s2);
return 0;
}
memcpy(common_str, str_to_add, common_len);
}
/** Make space in the common node array. */
if (!ldns_radix_array_space(common, array->str[common_len]) ||
!ldns_radix_array_space(common, str_to_add[common_len])) {
LDNS_FREE(common->array);
LDNS_FREE(common);
LDNS_FREE(common_str);
LDNS_FREE(s1);
LDNS_FREE(s2);
return 0;
}
/**
* The common node should go direct under the parent node.
* The added and existing nodes go under the common node.
*/
common->parent = array->edge->parent;
common->parent_index = array->edge->parent_index;
array->edge->parent = common;
array->edge->parent_index = array->str[common_len] -
common->offset;
add->parent = common;
add->parent_index = str_to_add[common_len] - common->offset;
common->array[array->edge->parent_index].edge = array->edge;
common->array[array->edge->parent_index].str = s1;
common->array[array->edge->parent_index].len = l1;
common->array[add->parent_index].edge = add;
common->array[add->parent_index].str = s2;
common->array[add->parent_index].len = l2;
LDNS_FREE(array->str);
array->edge = common;
array->str = common_str;
array->len = common_len;
}
return 1;
}
/**
* Check if one string prefix of other string.
* @param str1: one string.
* @param len1: one string length.
* @param str2: other string.
* @param len2: other string length.
* @return 1 if prefix, 0 otherwise.
*
*/
static int
ldns_radix_str_is_prefix(uint8_t* str1, radix_strlen_t len1,
uint8_t* str2, radix_strlen_t len2)
{
if (len1 == 0) {
return 1; /* empty prefix is also a prefix */
}
if (len1 > len2) {
return 0; /* len1 is longer so str1 cannot be a prefix */
}
return (memcmp(str1, str2, len1) == 0);
}
/**
* Return the number of bytes in common for the two strings.
* @param str1: one string.
* @param len1: one string length.
* @param str2: other string.
* @param len2: other string length.
* @return length of substring that the two strings have in common.
*
*/
static radix_strlen_t
ldns_radix_str_common(uint8_t* str1, radix_strlen_t len1,
uint8_t* str2, radix_strlen_t len2)
{
radix_strlen_t i, max = (len1<len2)?len1:len2;
for (i=0; i<max; i++) {
if (str1[i] != str2[i]) {
return i;
}
}
return max;
}
/**
* Find the next element in the subtree of this node.
* @param node: node.
* @return: node with next element.
*
*/
static ldns_radix_node_t*
ldns_radix_next_in_subtree(ldns_radix_node_t* node)
{
uint16_t i;
ldns_radix_node_t* next;
/** Try every subnode. */
for (i = 0; i < node->len; i++) {
if (node->array[i].edge) {
/** Node itself. */
if (node->array[i].edge->data) {
return node->array[i].edge;
}
/** Dive into subtree. */
next = ldns_radix_next_in_subtree(node->array[i].edge);
if (next) {
return next;
}
}
}
return NULL;
}
/**
* Find the previous element in the array of this node, from index.
* @param node: node.
* @param index: index.
* @return previous node from index.
*
*/
static ldns_radix_node_t*
ldns_radix_prev_from_index(ldns_radix_node_t* node, uint8_t index)
{
uint8_t i = index;
while (i > 0) {
i--;
if (node->array[i].edge) {
ldns_radix_node_t* prev =
ldns_radix_last_in_subtree_incl_self(node);
if (prev) {
return prev;
}
}
}
return NULL;
}
/**
* Find last node in subtree, or this node (if have data).
* @param node: node.
* @return last node in subtree, or this node, or NULL.
*
*/
static ldns_radix_node_t*
ldns_radix_last_in_subtree_incl_self(ldns_radix_node_t* node)
{
ldns_radix_node_t* last = ldns_radix_last_in_subtree(node);
if (last) {
return last;
} else if (node->data) {
return node;
}
return NULL;
}
/**
* Find last node in subtree.
* @param node: node.
* @return last node in subtree.
*
*/
static ldns_radix_node_t*
ldns_radix_last_in_subtree(ldns_radix_node_t* node)
{
int i;
/** Look for the most right leaf node. */
for (i=(int)(node->len)-1; i >= 0; i--) {
if (node->array[i].edge) {
/** Keep looking for the most right leaf node. */
if (node->array[i].edge->len > 0) {
ldns_radix_node_t* last =
ldns_radix_last_in_subtree(
node->array[i].edge);
if (last) {
return last;
}
}
/** Could this be the most right leaf node? */
if (node->array[i].edge->data) {
return node->array[i].edge;
}
}
}
return NULL;
}
/**
* Fix tree after deleting element.
* @param tree: tree.
* @param node: node with deleted element.
*
*/
static void
ldns_radix_del_fix(ldns_radix_t* tree, ldns_radix_node_t* node)
{
while (node) {
if (node->data) {
/** Thou should not delete nodes with data attached. */
return;
} else if (node->len == 1 && node->parent) {
/** Node with one child is fold back into. */
ldns_radix_cleanup_onechild(node);
return;
} else if (node->len == 0) {
/** Leaf node. */
ldns_radix_node_t* parent = node->parent;
if (!parent) {
/** The root is a leaf node. */
ldns_radix_node_free(node, NULL);
tree->root = NULL;
return;
}
/** Cleanup leaf node and continue with parent. */
ldns_radix_cleanup_leaf(node);
node = parent;
} else {
/**
* Node cannot be deleted, because it has edge nodes
* and no parent to fix up to.
*/
return;
}
}
/** Not reached. */
return;
}
/**
* Clean up a node with one child.
* @param node: node with one child.
*
*/
static void
ldns_radix_cleanup_onechild(ldns_radix_node_t* node)
{
uint8_t* join_str;
radix_strlen_t join_len;
uint8_t parent_index = node->parent_index;
ldns_radix_node_t* child = node->array[0].edge;
ldns_radix_node_t* parent = node->parent;
/** Node has one child, merge the child node into the parent node. */
assert(parent_index < parent->len);
join_len = parent->array[parent_index].len + node->array[0].len + 1;
join_str = LDNS_XMALLOC(uint8_t, join_len);
if (!join_str) {
/**
* Cleanup failed due to out of memory.
* This tree is now inefficient, with the empty node still
* existing, but it is still valid.
*/
return;
}
memcpy(join_str, parent->array[parent_index].str,
parent->array[parent_index].len);
join_str[parent->array[parent_index].len] = child->parent_index +
node->offset;
memmove(join_str + parent->array[parent_index].len+1,
node->array[0].str, node->array[0].len);
LDNS_FREE(parent->array[parent_index].str);
parent->array[parent_index].str = join_str;
parent->array[parent_index].len = join_len;
parent->array[parent_index].edge = child;
child->parent = parent;
child->parent_index = parent_index;
ldns_radix_node_free(node, NULL);
return;
}
/**
* Clean up a leaf node.
* @param node: leaf node.
*
*/
static void
ldns_radix_cleanup_leaf(ldns_radix_node_t* node)
{
uint8_t parent_index = node->parent_index;
ldns_radix_node_t* parent = node->parent;
/** Delete lead node and fix parent array. */
assert(parent_index < parent->len);
ldns_radix_node_free(node, NULL);
LDNS_FREE(parent->array[parent_index].str);
parent->array[parent_index].str = NULL;
parent->array[parent_index].len = 0;
parent->array[parent_index].edge = NULL;
/** Fix array in parent. */
if (parent->len == 1) {
ldns_radix_node_array_free(parent);
} else if (parent_index == 0) {
ldns_radix_node_array_free_front(parent);
} else {
ldns_radix_node_array_free_end(parent);
}
return;
}
/**
* Free a radix node.
* @param node: node.
* @param arg: user argument.
*
*/
static void
ldns_radix_node_free(ldns_radix_node_t* node, void* arg)
{
uint16_t i;
(void) arg;
if (!node) {
return;
}
for (i=0; i < node->len; i++) {
LDNS_FREE(node->array[i].str);
}
node->key = NULL;
node->klen = 0;
LDNS_FREE(node->array);
LDNS_FREE(node);
return;
}
/**
* Free select edge array.
* @param node: node.
*
*/
static void
ldns_radix_node_array_free(ldns_radix_node_t* node)
{
node->offset = 0;
node->len = 0;
LDNS_FREE(node->array);
node->array = NULL;
node->capacity = 0;
return;
}
/**
* Free front of select edge array.
* @param node: node.
*
*/
static void
ldns_radix_node_array_free_front(ldns_radix_node_t* node)
{
uint16_t i, n = 0;
/** Remove until a non NULL entry. */
while (n < node->len && node->array[n].edge == NULL) {
n++;
}
if (n == 0) {
return;
}
if (n == node->len) {
ldns_radix_node_array_free(node);
return;
}
assert(n < node->len);
assert((int) n <= (255 - (int) node->offset));
memmove(&node->array[0], &node->array[n],
(node->len - n)*sizeof(ldns_radix_array_t));
node->offset += n;
node->len -= n;
for (i=0; i < node->len; i++) {
if (node->array[i].edge) {
node->array[i].edge->parent_index = i;
}
}
ldns_radix_array_reduce(node);
return;
}
/**
* Free front of select edge array.
* @param node: node.
*
*/
static void
ldns_radix_node_array_free_end(ldns_radix_node_t* node)
{
uint16_t n = 0;
/** Shorten array. */
while (n < node->len && node->array[node->len-1-n].edge == NULL) {
n++;
}
if (n == 0) {
return;
}
if (n == node->len) {
ldns_radix_node_array_free(node);
return;
}
assert(n < node->len);
node->len -= n;
ldns_radix_array_reduce(node);
return;
}
/**
* Reduce the capacity of the array if needed.
* @param node: node.
*
*/
static void
ldns_radix_array_reduce(ldns_radix_node_t* node)
{
if (node->len <= node->capacity/2 && node->len != node->capacity) {
ldns_radix_array_t* a = LDNS_XMALLOC(ldns_radix_array_t,
node->len);
if (!a) {
return;
}
memcpy(a, node->array, sizeof(ldns_radix_array_t)*node->len);
LDNS_FREE(node->array);
node->array = a;
node->capacity = node->len;
}
return;
}
/**
* Return this element if it exists, the previous otherwise.
* @param node: from this node.
* @param result: result node.
*
*/
static void
ldns_radix_self_or_prev(ldns_radix_node_t* node, ldns_radix_node_t** result)
{
if (node->data) {
*result = node;
} else {
*result = ldns_radix_prev(node);
}
return;
}