Skip to content
Snippets Groups Projects
Commit 8d1f0569 authored by Marek Vavrusa's avatar Marek Vavrusa Committed by Gerrit Code Review
Browse files

Implemented HAT trie longest prefix match + tests.

General idea is regular search with keeping node stack.
If node is found, then return current node.
If node is htable, search for suffixes in htable.
If anything fails, walk up the visited nodes and check if
any visited node has value. If yes, then it is a prefix.

f.e. searching for 'abcd'
visited nodes: root -a-> {1:trie} -b-> {2:htable}
1. search htable for 'cd', 'c'
2. retrace to node {1}, does it have value?
3. yes, node1 is prefix 'ab' for 'abcd'

Change-Id: I622adbe9a127d8317b933af4bf56e1411e2d915f
parent 6c00e514
No related branches found
No related tags found
No related merge requests found
......@@ -209,6 +209,8 @@ src/tests/common/events_tests.c
src/tests/common/events_tests.h
src/tests/common/fdset_tests.c
src/tests/common/fdset_tests.h
src/tests/common/hattrie_tests.c
src/tests/common/hattrie_tests.h
src/tests/common/skiplist_tests.c
src/tests/common/skiplist_tests.h
src/tests/common/slab_tests.c
......
......@@ -105,6 +105,8 @@ unittests_SOURCES = \
tests/common/fdset_tests.h \
tests/common/skiplist_tests.c \
tests/common/skiplist_tests.h \
tests/common/hattrie_tests.c \
tests/common/hattrie_tests.h \
tests/common/slab_tests.c \
tests/common/slab_tests.h \
tests/knot/conf_tests.c \
......
......@@ -683,6 +683,55 @@ int hattrie_find_leq (hattrie_t* T, const char* key, size_t len, value_t** dst)
return ret;
}
int hattrie_find_lpr (hattrie_t* T, const char* key, size_t len, value_t** dst)
{
/* create node stack for traceback */
int ret = -1;
size_t sp = 0;
node_ptr bs[NODESTACK_INIT]; /* base stack (will be enough mostly) */
node_ptr *ns = bs; /* generic ptr, could point to new mem */
ns[sp] = T->root;
*dst = NULL;
/* consume trie nodes for key (thus building prefix chain) */
node_ptr node = hattrie_find_ns(&ns, &sp, NODESTACK_INIT, &key, &len);
if (node.flag == NULL) {
if (sp == 0) /* empty trie, no prefix match */
return -1;
node = ns[--sp]; /* dead end, pop node */
}
/* search for suffix in current node */
size_t suffix = len; /* suffix length */
if (*node.flag & NODE_TYPE_TRIE) {
*dst = &node.t->val; /* use current trie node value */
} else {
while (*dst == NULL) { /* find remainder in current ahtable */
*dst = ahtable_tryget(node.b, key, suffix);
if (suffix == 0)
break;
--suffix;
}
}
/* not in current node, need to traceback node stack */
while (*dst == NULL) {
node = ns[sp]; /* parent node, always a trie node type */
if (*node.flag & NODE_HAS_VAL)
*dst = &node.t->val;
if (sp == 0)
break;
--sp;
}
if (*dst) { /* prefix found? */
ret = 0;
}
if (ns != bs) free(ns);
return ret;
}
int hattrie_del(hattrie_t* T, const char* key, size_t len)
{
......
......@@ -66,6 +66,10 @@ value_t* hattrie_tryget (hattrie_t*, const char* key, size_t len);
* exist. Also set prev to point to previous node. */
int hattrie_find_leq (hattrie_t*, const char* key, size_t len, value_t** dst);
/** Find a longest prefix match. */
int hattrie_find_lpr (hattrie_t*, const char* key, size_t len, value_t** dst);
/** Delete a given key from trie. Returns 0 if successful or -1 if not found.
*/
int hattrie_del(hattrie_t* T, const char* key, size_t len);
......
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <time.h>
#include "tests/common/hattrie_tests.h"
#include "common/mempattern.h"
#include "common/hattrie/hat-trie.h"
static const char *alphabet = "abcdefghijklmn";
static char *randstr() {
unsigned len = (1 + rand() % 64) + 1; /* (1-64) + '\0' */
char *s = xmalloc(len * sizeof(char));
for (unsigned i = 0; i < len - 1; ++i) {
s[i] = alphabet[rand() % strlen(alphabet)];
}
s[len - 1] = '\0';
return s;
}
static int hattrie_tests_count(int argc, char *argv[]);
static int hattrie_tests_run(int argc, char *argv[]);
/*
* Unit API.
*/
unit_api hattrie_tests_api = {
"HAT trie",
&hattrie_tests_count,
&hattrie_tests_run
};
/*
* Unit implementation.
*/
static const int HAT_TEST_COUNT = 6;
static int hattrie_tests_count(int argc, char *argv[])
{
return HAT_TEST_COUNT;
}
static int hattrie_tests_run(int argc, char *argv[])
{
/* Interesting intems. */
unsigned count = 10;
const char *items[] = {
"abcd",
"abc",
"ab",
"a",
"abcdefghijklmnopqrstuvw",
"abAcd",
"abcA",
"abA",
"Aab",
"A"
};
/* Dummy items. */
srand(time(NULL));
unsigned dummy_count = 10000;
char **dummy = xmalloc(sizeof(char*) * dummy_count);
for (unsigned i = 0; i < dummy_count; ++i) {
dummy[i] = randstr();
}
/* Test 1: Create */
unsigned passed = 1;
value_t *v = NULL;
hattrie_t *t = hattrie_create();
ok(t != NULL, "hattrie: create");
/* Test 2: Insert */
passed = 1;
for (unsigned i = 0; i < count; ++i) {
v = hattrie_get(t, items[i], strlen(items[i]));
if (!v) {
passed = 0;
break;
}
*v = (value_t)items[i];
}
ok(passed, "hattrie: insert");
/* Test 3: Insert dummy. */
passed = 1;
for (unsigned i = 0; i < dummy_count; ++i) {
v = hattrie_get(t, dummy[i], strlen(dummy[i]));
if (!v) {
passed = 0;
break;
}
if (*v == NULL) {
*v = dummy[i];
}
}
ok(passed, "hattrie: dummy insert");
/* Test 4: Lookup */
passed = 1;
for (unsigned i = 0; i < count; ++i) {
v = hattrie_tryget(t, items[i], strlen(items[i]));
if (!v || *v != items[i]) {
diag("hattrie: mismatch on element '%u'", i);
passed = 0;
break;
}
}
ok(passed, "hattrie: lookup");
/* Test 5: LPR lookup */
unsigned lpr_count = 5;
const char *lpr[] = {
"abcdZ",
"abcZ",
"abZ",
"aZ",
"abcdefghijklmnopqrstuvw"
};
passed = 1;
for (unsigned i = 0; i < lpr_count; ++i) {
int ret = hattrie_find_lpr(t, lpr[i], strlen(lpr[i]), &v);
if (!v || ret != 0 || *v != items[i]) {
diag("hattrie: lpr='%s' mismatch lpr(%s) != %s",
!v ? NULL : *v, lpr[i], items[i]);
passed = 0;
break;
}
}
ok(passed, "hattrie: longest prefix match");
/* Test 6: false LPR lookup */
const char *false_lpr = "Z";
int ret = hattrie_find_lpr(t, false_lpr, strlen(false_lpr), &v);
ok(ret != 0 && v == NULL, "hattrie: non-existent prefix lookup\n");
for (unsigned i = 0; i < dummy_count; ++i) {
free(dummy[i]);
}
free(dummy);
hattrie_free(t);
return 0;
}
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _KNOTD_HATTRIE_TESTS_H_
#define _KNOTD_HATTRIE_TESTS_H_
#include "common/libtap/tap_unit.h"
/* Unit API. */
unit_api hattrie_tests_api;
#endif /* _KNOTD_HATTRIE_TESTS_H_ */
......@@ -21,6 +21,7 @@
// Units to test
#include "tests/common/slab_tests.h"
#include "tests/common/skiplist_tests.h"
#include "tests/common/hattrie_tests.h"
#include "tests/common/events_tests.h"
#include "tests/common/acl_tests.h"
#include "tests/common/fdset_tests.h"
......@@ -54,6 +55,7 @@ int main(int argc, char *argv[])
&journal_tests_api, //! Journal unit
&slab_tests_api, //! SLAB allocator unit
&skiplist_tests_api, //! Skip list unit
&hattrie_tests_api, //! HAT trie unit
&dthreads_tests_api, //! DThreads testing unit
&events_tests_api, //! Events testing unit
&acl_tests_api, //! ACLs
......@@ -75,7 +77,7 @@ int main(int argc, char *argv[])
&dname_tests_api,
&ztree_tests_api,
&sign_tests_api, //! Key manipulation.
&rrset_tests_api,
&rrset_tests_api,
NULL
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment