mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-13 05:41:26 +01:00
5afab0e5e5
Merge commit 'cf3e3d5bd0a1fae39c74c7db5a4e8b10732d0766' Reviewed by: emaste Differential Revision: https://reviews.freebsd.org/D40226
1594 lines
37 KiB
C
1594 lines
37 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 */
|
|
LDNS_FREE(add);
|
|
if (prefix->data) {
|
|
/* Element already exists */
|
|
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)) {
|
|
LDNS_FREE(common);
|
|
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)) {
|
|
LDNS_FREE(common);
|
|
LDNS_FREE(s1);
|
|
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;
|
|
}
|