Commit c75de663 authored by Daniel Salzman's avatar Daniel Salzman

zonedb: replace hhash with qp-trie

parent 2aab27c4
......@@ -224,7 +224,7 @@ static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server)
assert(server);
knot_zonedb_t *db_old = server->zone_db;
knot_zonedb_t *db_new = knot_zonedb_new(conf_id_count(conf, C_ZONE));
knot_zonedb_t *db_new = knot_zonedb_new();
if (!db_new) {
return NULL;
}
......@@ -286,11 +286,10 @@ static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
bool full = !(conf->io.flags & CONF_IO_FACTIVE) ||
(conf->io.flags & CONF_IO_FRLD_ZONES);
knot_zonedb_iter_t it;
knot_zonedb_iter_begin(db_old, &it);
knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old);
while (!knot_zonedb_iter_finished(&it)) {
zone_t *zone = knot_zonedb_iter_val(&it);
while (!knot_zonedb_iter_finished(it)) {
zone_t *zone = knot_zonedb_iter_val(it);
if (full) {
/* Check if reloaded (reused contents). */
......@@ -310,9 +309,11 @@ static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
/* Completely reused zone. */
}
knot_zonedb_iter_next(&it);
knot_zonedb_iter_next(it);
}
knot_zonedb_iter_free(it);
if (full) {
knot_zonedb_deep_free(&db_old);
} else {
......@@ -343,9 +344,6 @@ void zonedb_reload(conf_t *conf, server_t *server)
return;
}
/* Rebuild zone database search stack. */
knot_zonedb_build_index(db_new);
/* Switch the databases. */
knot_zonedb_t **db_current = &server->zone_db;
knot_zonedb_t *db_old = rcu_xchg_pointer(db_current, db_new);
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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
......@@ -14,12 +14,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include <stdlib.h>
#include "knot/zone/zonedb.h"
#include "libknot/packet/wire.h"
#include "contrib/macros.h"
#include "contrib/mempattern.h"
#include "contrib/ucw/mempool.h"
......@@ -44,24 +43,22 @@ static void discard_zone(zone_t *zone)
zone_free(&zone);
}
knot_zonedb_t *knot_zonedb_new(uint32_t size)
knot_zonedb_t *knot_zonedb_new(void)
{
/* Create memory pool context. */
knot_mm_t mm = {0};
mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
knot_zonedb_t *db = mm_alloc(&mm, sizeof(knot_zonedb_t));
knot_zonedb_t *db = calloc(1, sizeof(knot_zonedb_t));
if (db == NULL) {
return NULL;
}
db->maxlabels = 0;
db->hash = hhash_create_mm((size + 1) * 2, &mm);
if (db->hash == NULL) {
mm_free(&mm, db);
mm_ctx_mempool(&db->mm, MM_DEFAULT_BLKSIZE);
db->trie = trie_create(&db->mm);
if (db->trie == NULL) {
mp_delete(db->mm.ctx);
free(db);
return NULL;
}
memcpy(&db->mm, &mm, sizeof(knot_mm_t));
return db;
}
......@@ -71,12 +68,13 @@ int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone)
return KNOT_EINVAL;
}
int name_size = knot_dname_size(zone->name);
if (name_size < 0) {
return KNOT_EINVAL;
}
assert(zone->name);
uint8_t lf[KNOT_DNAME_MAXLEN];
knot_dname_lf(lf, zone->name, NULL);
*trie_get_ins(db->trie, (char *)lf + 1, *lf) = zone;
return hhash_insert(db->hash, (const char*)zone->name, name_size, zone);
return KNOT_EOK;
}
int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name)
......@@ -85,87 +83,54 @@ int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name)
return KNOT_EINVAL;
}
/* Can't guess maximum label count now. */
db->maxlabels = KNOT_DNAME_MAXLABELS;
/* Attempt to remove zone. */
int name_size = knot_dname_size(zone_name);
return hhash_del(db->hash, (const char*)zone_name, name_size);
}
int knot_zonedb_build_index(knot_zonedb_t *db)
{
if (db == NULL) {
return KNOT_EINVAL;
}
uint8_t lf[KNOT_DNAME_MAXLEN];
knot_dname_lf(lf, zone_name, NULL);
/* Rebuild order index. */
hhash_build_index(db->hash);
/* Calculate maxlabels. */
db->maxlabels = 0;
knot_zonedb_iter_t it;
knot_zonedb_iter_begin(db, &it);
while (!knot_zonedb_iter_finished(&it)) {
zone_t *zone = knot_zonedb_iter_val(&it);
db->maxlabels = MAX(db->maxlabels, knot_dname_labels(zone->name, NULL));
knot_zonedb_iter_next(&it);
trie_val_t *rval = trie_get_try(db->trie, (char *)lf + 1, *lf);
if (rval == NULL) {
return KNOT_ENOENT;
}
return KNOT_EOK;
}
static value_t *find_name(knot_zonedb_t *db, const knot_dname_t *dname, uint16_t size)
{
assert(db);
assert(dname);
return hhash_find(db->hash, (const char*)dname, size);
return trie_del(db->trie, (char *)lf + 1, *lf, NULL);
}
zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name)
{
int name_size = knot_dname_size(zone_name);
if (!db || name_size < 1) {
if (db == NULL) {
return NULL;
}
value_t *ret = find_name(db, zone_name, name_size);
if (ret == NULL) {
uint8_t lf[KNOT_DNAME_MAXLEN];
knot_dname_lf(lf, zone_name, NULL);
trie_val_t *val = trie_get_try(db->trie, (char *)lf + 1, *lf);
if (val == NULL) {
return NULL;
}
return *ret;
return *val;
}
zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *dname)
zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name)
{
if (db == NULL || dname == NULL) {
if (db == NULL || zone_name == NULL) {
return NULL;
}
/* We know we have at most N label zones, so let's compare only those
* N last labels. */
int zone_labels = knot_dname_labels(dname, NULL);
while (zone_labels > db->maxlabels) {
dname = knot_wire_next_label(dname, NULL);
--zone_labels;
}
uint8_t lf[KNOT_DNAME_MAXLEN];
/* Compare possible suffixes. */
value_t *val = NULL;
int name_size = knot_dname_size(dname);
while (name_size > 0) { /* Include root label. */
val = find_name(db, dname, name_size);
while (true) {
knot_dname_lf(lf, zone_name, NULL);
trie_val_t *val = trie_get_try(db->trie, (char *)lf + 1, *lf);
if (val != NULL) {
return *val;
} else if (zone_name[0] == 0) {
return NULL;
}
/* Next label */
name_size -= (dname[0] + 1);
dname = knot_wire_next_label(dname, NULL);
zone_name = knot_wire_next_label(zone_name, NULL);
}
return NULL;
}
size_t knot_zonedb_size(const knot_zonedb_t *db)
......@@ -174,7 +139,7 @@ size_t knot_zonedb_size(const knot_zonedb_t *db)
return 0;
}
return db->hash->weight;
return trie_weight(db->trie);
}
void knot_zonedb_free(knot_zonedb_t **db)
......@@ -184,6 +149,7 @@ void knot_zonedb_free(knot_zonedb_t **db)
}
mp_delete((*db)->mm.ctx);
free(*db);
*db = NULL;
}
......@@ -193,10 +159,6 @@ void knot_zonedb_deep_free(knot_zonedb_t **db)
return;
}
/* Reindex for iteration. */
knot_zonedb_build_index(*db);
/* Free zones and database. */
knot_zonedb_foreach(*db, discard_zone);
knot_zonedb_free(db);
}
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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
......@@ -16,56 +16,41 @@
/*!
* \file
*
* \brief Zone database structure and API for manipulating it.
*
* Zone database groups several zones and provides functions for finding
* suitable zone for a domain name, for searching in a particular zone, etc.
*
* \addtogroup zone
* @{
* \brief Zone database represents a list of managed zones.
*/
#pragma once
#include "knot/zone/zone.h"
#include "libknot/dname.h"
#include "contrib/hhash.h"
#include "contrib/qp-trie/trie.h"
/*
* Zone DB represents a list of managed zones.
* Hashing should be avoided as it is expensive when only a small number of
* zones is present (TLD case). Fortunately hhash is able to do linear scan if
* it has only a handful of names present. Furthermore, we track the name with
* the most labels in the database. So if we have for example a 'a.b.' in the
* database and search for 'c.d.a.b.' we can trim the 'c.d.' and search for
* the suffix as we now there can't be a closer match.
*/
typedef struct {
uint16_t maxlabels;
hhash_t *hash;
trie_t *trie;
knot_mm_t mm;
} knot_zonedb_t;
/*
* Mapping of iterators to internal data structure.
*/
typedef hhash_iter_t knot_zonedb_iter_t;
#define knot_zonedb_iter_begin(db, it) hhash_iter_begin((db)->hash, it, true)
#define knot_zonedb_iter_finished(it) hhash_iter_finished(it)
#define knot_zonedb_iter_next(it) hhash_iter_next(it)
#define knot_zonedb_iter_val(it) *hhash_iter_val(it)
typedef trie_it_t knot_zonedb_iter_t;
#define knot_zonedb_iter_begin(db) trie_it_begin((db)->trie)
#define knot_zonedb_iter_finished(it) trie_it_finished(it)
#define knot_zonedb_iter_next(it) trie_it_next(it)
#define knot_zonedb_iter_free(it) trie_it_free(it)
#define knot_zonedb_iter_val(it) *trie_it_val(it)
/*
* Simple foreach() access with callback and variable number of callback params.
*/
#define knot_zonedb_foreach(db, callback, ...) \
{ \
knot_zonedb_iter_t it; \
knot_zonedb_iter_begin((db), &it); \
while(!knot_zonedb_iter_finished(&it)) { \
callback((zone_t *)knot_zonedb_iter_val(&it), ##__VA_ARGS__); \
knot_zonedb_iter_next(&it); \
knot_zonedb_iter_t *it = knot_zonedb_iter_begin((db)); \
while(!knot_zonedb_iter_finished(it)) { \
callback((zone_t *)knot_zonedb_iter_val(it), ##__VA_ARGS__); \
knot_zonedb_iter_next(it); \
} \
knot_zonedb_iter_free(it); \
}
/*!
......@@ -74,7 +59,7 @@ typedef hhash_iter_t knot_zonedb_iter_t;
* \return Pointer to the created zone database structure or NULL if an error
* occurred.
*/
knot_zonedb_t *knot_zonedb_new(uint32_t size);
knot_zonedb_t *knot_zonedb_new(void);
/*!
* \brief Adds new zone to the database.
......@@ -98,11 +83,6 @@ int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone);
*/
int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name);
/*!
* \brief Build zone stack for faster lookup.
*/
int knot_zonedb_build_index(knot_zonedb_t *db);
/*!
* \brief Finds zone exactly matching the given zone name.
*
......@@ -123,7 +103,7 @@ zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name);
* \retval Zone in which the domain name should be present or NULL if no such
* zone is found.
*/
zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *dname);
zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name);
size_t knot_zonedb_size(const knot_zonedb_t *db);
......@@ -141,5 +121,3 @@ void knot_zonedb_free(knot_zonedb_t **db);
* \param db Zone database to be destroyed.
*/
void knot_zonedb_deep_free(knot_zonedb_t **db);
/*! @} */
......@@ -55,9 +55,8 @@ static inline void create_root_zone(server_t *server, knot_mm_t *mm)
/* Switch zone db. */
knot_zonedb_free(&server->zone_db);
server->zone_db = knot_zonedb_new(1);
server->zone_db = knot_zonedb_new();
knot_zonedb_insert(server->zone_db, root);
knot_zonedb_build_index(server->zone_db);
}
/* Create fake server. */
......
/* Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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
......@@ -37,7 +37,7 @@ static const char *zone_list[ZONE_COUNT] = {
int main(int argc, char *argv[])
{
plan(6);
plan_lazy();
/* Create database. */
char buf[KNOT_DNAME_MAXLEN];
......@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
size_t nr_passed = 0;
knot_dname_t *dname = NULL;
zone_t *zones[ZONE_COUNT] = {0};
knot_zonedb_t *db = knot_zonedb_new(ZONE_COUNT);
knot_zonedb_t *db = knot_zonedb_new();
ok(db != NULL, "zonedb: new");
/* Populate. */
......@@ -65,9 +65,6 @@ int main(int argc, char *argv[])
}
ok(nr_passed == ZONE_COUNT, "zonedb: add zones");
/* Build search index. */
ok(knot_zonedb_build_index(db) == KNOT_EOK, "zonedb: build search index");
/* Lookup of exact names. */
nr_passed = 0;
for (unsigned i = 0; i < ZONE_COUNT; ++i) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment