mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-17 10:26:15 +00:00
986ba33c7a
I've been holding back on this because 1.7.0 requires OpenSSL 1.1.0 or newer for full DANE support. But we can't wait forever, and nothing in base uses DANE anyway, so here we go.
1591 lines
36 KiB
C
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 COPYRIGHT
|
|
* HOLDER 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, const 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, const 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, const 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(const 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(const 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, const 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;
|
|
}
|