summaryrefslogtreecommitdiff
path: root/radix.c
diff options
context:
space:
mode:
Diffstat (limited to 'radix.c')
-rw-r--r--radix.c1590
1 files changed, 1590 insertions, 0 deletions
diff --git a/radix.c b/radix.c
new file mode 100644
index 0000000..43f7365
--- /dev/null
+++ b/radix.c
@@ -0,0 +1,1590 @@
+/*
+ * 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;
+}