diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index 90b0354887629cff94ae4cd6be627d9c1039121c..5350bd5b06fac284133b2ddad2ba578f722586d8 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -108,7 +108,7 @@ static int remote_rdata_apply(server_t *s, remote_cmdargs_t* a, remote_zonef_t * const knot_dname_t *dn = knot_rdata_ns_name(rr, i); rcu_read_lock(); - zone = knot_zonedb_find_zone(ns->zone_db, dn); + zone = knot_zonedb_find(ns->zone_db, dn); if (cb(s, zone) != KNOT_EOK) { a->rc = KNOT_RCODE_SERVFAIL; } @@ -254,14 +254,16 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a) int ret = KNOT_EOK; rcu_read_lock(); knot_nameserver_t *ns = s->nameserver; - const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); - for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - zonedata_t *zd = (zonedata_t *)zones[i]->data; + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(ns->zone_db, &it); + while(!knot_zonedb_iter_finished(&it)) { + const knot_zone_t *zone = knot_zonedb_iter_val(&it); + zonedata_t *zd = (zonedata_t *)zone->data; /* Fetch latest serial. */ const knot_rrset_t *soa_rrs = 0; uint32_t serial = 0; - knot_zone_contents_t *contents = knot_zone_get_contents(zones[i]); + knot_zone_contents_t *contents = knot_zone_get_contents(zone); if (contents) { soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents), KNOT_RRTYPE_SOA); @@ -333,7 +335,7 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a) rb -= n; dst += n; - + knot_zonedb_iter_next(&it); } rcu_read_unlock(); @@ -377,9 +379,11 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a) dbg_server_verb("remote: flushing all zones\n"); rcu_read_lock(); knot_nameserver_t *ns = s->nameserver; - const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); - for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - ret = remote_zone_flush(s, zones[i]); + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(ns->zone_db, &it); + while(!knot_zonedb_iter_finished(&it)) { + ret = remote_zone_flush(s, knot_zonedb_iter_val(&it)); + knot_zonedb_iter_next(&it); } rcu_read_unlock(); return ret; diff --git a/src/knot/main.c b/src/knot/main.c index 704b5a89d649b3421139ecc6305032e6ad9596cb..7866fa928ba06514185575454d6beecf32d85edd 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -325,8 +325,7 @@ int main(int argc, char **argv) int res = 0; log_server_info("Starting server...\n"); if ((server_start(server)) == KNOT_EOK) { - size_t zcount = server->nameserver->zone_db->count; - if (!zcount) { + if (!knot_zonedb_size(server->nameserver->zone_db)) { log_server_warning("Server started, but no zones served.\n"); } diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c index d93fee8554d1df43708cc28e36510d62c8712590..cb1f98b158d51b2027085620039d2d558789a5e5 100644 --- a/src/knot/server/notify.c +++ b/src/knot/server/notify.c @@ -261,7 +261,7 @@ int notify_process_request(knot_nameserver_t *ns, unsigned serial = 0; const knot_dname_t *qname = knot_packet_qname(notify); rcu_read_lock(); /* z */ - const knot_zone_t *z = knot_zonedb_find_zone_for_name(ns->zone_db, qname); + const knot_zone_t *z = knot_zonedb_find_suffix(ns->zone_db, qname); if (z != NULL) { ret = notify_check_and_schedule(ns, z, from); const knot_rrset_t *soa_rr = NULL; diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 3d89002de55cfae5fefa839a579d5a9f67959a3b..19334028f60ebbd2eff98c4263a513f586b73821 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -470,21 +470,23 @@ int server_refresh(server_t *server) rcu_read_lock(); knot_nameserver_t *ns = server->nameserver; evsched_t *sch = ((server_t *)knot_ns_get_data(ns))->sched; - const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(ns->zone_db, &it); + unsigned count = 0; + while(!knot_zonedb_iter_finished(&it)) { + const knot_zone_t *zone = knot_zonedb_iter_val(&it); + zonedata_t *zd = (zonedata_t *)zone->data; - /* REFRESH zones. */ - for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - zonedata_t *zd = (zonedata_t *)zones[i]->data; - if (zd == NULL) { - continue; - } /* Expire REFRESH timer. */ - if (zd->xfr_in.timer) { + if (zd && zd->xfr_in.timer) { evsched_cancel(sch, zd->xfr_in.timer); evsched_schedule(sch, zd->xfr_in.timer, - tls_rand() * 500 + i/2); + tls_rand() * 500 + count/2); /* Cumulative delay. */ + ++count; } + + knot_zonedb_iter_next(&it); } /* Unlock RCU. */ diff --git a/src/knot/server/zone-load.c b/src/knot/server/zone-load.c index d7f68e2431df2f1ac95afdf77224a03659ee2114..062974119dfc835051cc4c001d2215be6beb07b2 100644 --- a/src/knot/server/zone-load.c +++ b/src/knot/server/zone-load.c @@ -590,7 +590,7 @@ static int update_zone(knot_zone_t **dst, conf_zone_t *conf, knot_nameserver_t * return KNOT_ENOMEM; } - knot_zone_t *old_zone = knot_zonedb_find_zone(ns->zone_db, apex); + knot_zone_t *old_zone = knot_zonedb_find(ns->zone_db, apex); knot_zone_t *new_zone = get_zone(old_zone, apex, conf, ns); knot_dname_free(&apex); @@ -698,7 +698,7 @@ static int zone_loader_thread(dthread_t *thread) /* Insert into database if properly loaded. */ pthread_mutex_lock(&ctx->lock); if (ret == KNOT_EOK) { - if (knot_zonedb_add_zone(ctx->db_new, zone) != KNOT_EOK) { + if (knot_zonedb_insert(ctx->db_new, zone) != KNOT_EOK) { log_server_error("Failed to insert zone '%s' " "into database.\n", zone_config->name); knot_zone_deep_free(&zone); @@ -784,21 +784,22 @@ static knot_zonedb_t *load_zonedb(knot_nameserver_t *ns, const conf_t *conf) static int remove_zones(const knot_zonedb_t *db_new, knot_zonedb_t *db_old) { - unsigned new_zone_count = db_new->count; - const knot_zone_t **new_zones = knot_zonedb_zones(db_new); - const knot_zone_t *old_zone = NULL; - for (unsigned i = 0; i < new_zone_count; ++i) { + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(db_new, &it); + while(!knot_zonedb_iter_finished(&it)) { + const knot_zone_t *old_zone = NULL; + const knot_zone_t *new_zone = knot_zonedb_iter_val(&it); + const knot_dname_t *zone_name = new_zone->name; /* try to find the new zone in the old DB * if the pointers match, remove the zone from old DB */ - old_zone = knot_zonedb_find_zone(db_old, knot_zone_name(new_zones[i])); - if (old_zone == new_zones[i]) { - /* Remove from zone db. */ - knot_zone_t * rm = knot_zonedb_remove_zone(db_old, - knot_zone_name(old_zone)); - assert(rm == old_zone); + old_zone = knot_zonedb_find(db_old, zone_name); + if (old_zone == new_zone) { + knot_zonedb_del(db_old, zone_name); } + + knot_zonedb_iter_next(&it); } return KNOT_EOK; @@ -832,7 +833,7 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns, log_server_warning("Failed to load zones.\n"); return KNOT_ENOMEM; } else { - size_t loaded = knot_zonedb_zone_count(db_new); + size_t loaded = knot_zonedb_size(db_new); log_server_info("Loaded %zu out of %d zones.\n", loaded, conf->zones_count); if (loaded != conf->zones_count) { diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index 6541777ce52c37ef4d9cfe0adcc476ee5e45c857..e56880f72b8b1e7cc2f7754aefc93f6f9041228b 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -1780,7 +1780,7 @@ int zones_process_response(knot_nameserver_t *nameserver, rcu_read_lock(); const knot_dname_t *zone_name = knot_packet_qname(packet); /*! \todo Change the access to the zone db. */ - knot_zone_t *zone = knot_zonedb_find_zone( + knot_zone_t *zone = knot_zonedb_find( nameserver->zone_db, zone_name); @@ -2023,14 +2023,13 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data) knot_zonedb_deep_free(&old_db); /* Update events scheduled for zone. */ - knot_zone_t *zone = NULL; - const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); - - /* REFRESH zones. */ - for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - zone = (knot_zone_t *)zones[i]; + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(ns->zone_db, &it); + while(!knot_zonedb_iter_finished(&it)) { + knot_zone_t *zone = knot_zonedb_iter_val(&it); zones_schedule_refresh(zone, 0); /* Now. */ zones_schedule_notify(zone); + knot_zonedb_iter_next(&it); } return KNOT_EOK; @@ -2919,7 +2918,7 @@ int zones_do_diff_and_sign(const conf_zone_t *z, knot_zone_t *zone, zones_cancel_dnssec(zone); /* Calculate differences. */ rcu_read_lock(); - knot_zone_t *z_old = knot_zonedb_find_zone(ns->zone_db, + knot_zone_t *z_old = knot_zonedb_find(ns->zone_db, zone->name); /* Ensure both new and old have zone contents. */ knot_zone_contents_t *zc = knot_zone_get_contents(zone); diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 17e68a140105182885ee6b157b577d1c6a291ef6..839d002ac27d6a31cc87de9df7cb2676e6042a2c 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -84,14 +84,14 @@ static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb, */ if (qtype == KNOT_RRTYPE_DS) { const knot_dname_t *parent = knot_wire_next_label(qname, NULL); - zone = knot_zonedb_find_zone_for_name(zdb, parent); + zone = knot_zonedb_find_suffix(zdb, parent); /* If zone does not exist, search for its parent zone, this will later result to NODATA answer. */ if (zone == NULL) { - zone = knot_zonedb_find_zone_for_name(zdb, qname); + zone = knot_zonedb_find_suffix(zdb, qname); } } else { - zone = knot_zonedb_find_zone_for_name(zdb, qname); + zone = knot_zonedb_find_suffix(zdb, qname); } return zone; @@ -3580,7 +3580,7 @@ dbg_ns_exec_verb( free(name_str); ); // find zone - *zone = knot_zonedb_find_zone(zonedb, qname); + *zone = knot_zonedb_find(zonedb, qname); return KNOT_EOK; } @@ -3707,7 +3707,7 @@ dbg_ns_exec_verb( free(name_str); ); // find zone in which to search for the name - knot_zone_t *zone = knot_zonedb_find_zone(zonedb, qname); + knot_zone_t *zone = knot_zonedb_find(zonedb, qname); // if no zone found, return NotAuth if (zone == NULL) { diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c index 3769650ca012e1f7cc67190e3da801d662bbfde4..0099265090bd6ceb77aafe2b990c844e04d062dd 100644 --- a/src/libknot/zone/zonedb.c +++ b/src/libknot/zone/zonedb.c @@ -20,7 +20,6 @@ #include <urcu.h> -#include "common/binsearch.h" #include "libknot/common.h" #include "libknot/zone/zone.h" #include "libknot/zone/zonedb.h" @@ -28,16 +27,8 @@ #include "libknot/util/wire.h" #include "libknot/zone/node.h" #include "libknot/util/debug.h" - -/* Array sorter generator. */ -static int knot_zonedb_cmp(const knot_dname_t* d1, const knot_dname_t *d2); -#define ASORT_PREFIX(X) knot_zonedb_##X -#define ASORT_KEY_TYPE knot_zone_t* -#define ASORT_LT(x, y) (knot_zonedb_cmp((x)->name, (y)->name) < 0) -#include "common/array-sort.h" - -/* Defines */ -#define BSEARCH_THRESHOLD 8 /* >= N for which binary search is favoured */ +#include "common/mempattern.h" +#include "common/mempool.h" /*----------------------------------------------------------------------------*/ /* Non-API functions */ @@ -51,216 +42,124 @@ static void delete_zone_from_db(knot_zone_t *zone) knot_zone_release(zone); } -/*! \brief Zone database zone name compare function. */ -static int knot_zonedb_cmp(const knot_dname_t* d1, const knot_dname_t *d2) -{ - int a_labels = knot_dname_labels(d1, NULL); - int b_labels = knot_dname_labels(d2, NULL); - - /* Lexicographic order. */ - if (a_labels == b_labels) { - return knot_dname_cmp(d1, d2); - } - - /* Name with more labels goes first. */ - return b_labels - a_labels; -} - -/*! \brief Find an equal name in sorted array (binary search). */ -#define ZONEDB_LEQ(arr,i,x) (knot_zonedb_cmp(((arr)[i])->name, (x)) <= 0) -static long knot_zonedb_binsearch(knot_zone_t **arr, unsigned count, - const knot_dname_t *name) -{ - int k = BIN_SEARCH_FIRST_GE_CMP(arr, count, ZONEDB_LEQ, name) - 1; - if (k > -1 && knot_dname_is_equal(arr[k]->name, name)) { - return k; - } - - return -1; - -} - -/*! \brief Find an equal name in an array (linear search). - * \note Linear search uses simple name equality test which could be - * faster than canonical compare and therefore more efficient for - * smaller arrays. - */ -static long knot_zonedb_linear_search(knot_zone_t **arr, unsigned count, - const knot_dname_t *name) { - for (unsigned i = 0; i < count; ++i) { - if (knot_dname_is_equal(arr[i]->name, name)) { - return i; - } - } - return -1; -} - -/*! \brief Zone array search. */ -static long knot_zonedb_array_search(knot_zone_t **arr, unsigned count, - const knot_dname_t *name) -{ - if (count < BSEARCH_THRESHOLD) { - return knot_zonedb_linear_search(arr, count, name); - } else { - return knot_zonedb_binsearch(arr, count, name); - } -} - /*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ -knot_zonedb_t *knot_zonedb_new(unsigned size) +knot_zonedb_t *knot_zonedb_new(uint32_t size) { - knot_zonedb_t *db = malloc(sizeof(knot_zonedb_t)); - CHECK_ALLOC_LOG(db, NULL); - - memset(db, 0, sizeof(knot_zonedb_t)); - db->reserved = size; - db->array = malloc(size * sizeof(knot_zone_t*)); - if (db->array == NULL) { - free(db); + /* Create memory pool context. */ + mm_ctx_t mm; + mm.ctx = mp_new(4096); + mm.alloc = (mm_alloc_t)mp_alloc; + mm.free = mm_nofree; + knot_zonedb_t *db = mm.alloc(mm.ctx, 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(db); return NULL; } + memcpy(&db->mm, &mm, sizeof(mm_ctx_t)); return db; } /*----------------------------------------------------------------------------*/ -int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone) +int knot_zonedb_insert(knot_zonedb_t *db, knot_zone_t *zone) { if (db == NULL || zone == NULL) { return KNOT_EINVAL; } - /* Invalidate search index. */ - db->stack_height = 0; - - /* Create new record. */ - assert(db->count < db->reserved); /* Should be already checked. */ - db->array[db->count++] = zone; - - return KNOT_EOK; + int name_size = knot_dname_size(zone->name); + return hhash_insert(db->hash, (const char*)zone->name, name_size, zone); } /*----------------------------------------------------------------------------*/ -knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db, - const knot_dname_t *zone_name) +int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name) { if (db == NULL || zone_name == NULL) { - return NULL; - } - - /* Find the possible zone to remove. */ - int pos = knot_zonedb_array_search(db->array, db->count, zone_name); - if (pos < 0) { - return NULL; + return KNOT_EINVAL; } - - /* Invalidate search index. */ - db->stack_height = 0; - /* Move rest of the array to not break the ordering. */ - knot_zone_t *removed_zone = db->array[pos]; - unsigned remainder = (db->count - (pos + 1)) * sizeof(knot_zone_t*); - memmove(db->array + pos, db->array + pos + 1, remainder); - --db->count; - - return removed_zone; + /* 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) { - return KNOT_EINVAL; - } + /* Rebuild order index. */ + hhash_build_index(db->hash); - /* First, sort all zones based on the label count first and lexicographic - * order second. The name with most labels goes first. - * i.e. {a, a.b, a.c, b } -> {a.b, a.c, a, b} */ - knot_zonedb_sort(db->array, db->count); - - /* Scan the array and group names with the same label count together. */ - int prev_label_count = -1; - int current_label_count = -1; - knot_zone_t **endp = db->array + db->count; - knot_zonedb_stack_t *stack_top = db->stack - 1; /* Before actual stack. */ - db->stack_height = 0; - - for (knot_zone_t **zone = db->array; zone != endp; ++zone) { - /* Insert into current label count group. */ - current_label_count = knot_dname_labels((*zone)->name, NULL); - if (current_label_count == prev_label_count) { - ++stack_top->count; - continue; - } - - /* Begin new label count group. */ - ++stack_top; - ++db->stack_height; - stack_top->count = 1; - stack_top->labels = current_label_count; - stack_top->array = zone; - prev_label_count = current_label_count; - + /* Calculate maxlabels. */ + db->maxlabels = 0; + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(db, &it); + while (!knot_zonedb_iter_finished(&it)) { + knot_zone_t *zone = knot_zonedb_iter_val(&it); + db->maxlabels = MAX(db->maxlabels, knot_dname_labels(zone->name, NULL)); + knot_zonedb_iter_next(&it); } - + return KNOT_EOK; } /*----------------------------------------------------------------------------*/ -knot_zone_t *knot_zonedb_find_zone(knot_zonedb_t *db, - const knot_dname_t *zone_name) +knot_zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name) { if (!db || !zone_name) { return NULL; } - int pos = knot_zonedb_array_search(db->array, db->count, zone_name); - if (pos < 0) { + int name_size = knot_dname_size(zone_name); + value_t *ret = hhash_find(db->hash, (const char*)zone_name, name_size); + if (ret == NULL) { return NULL; } - return db->array[pos]; + return *ret; } /*----------------------------------------------------------------------------*/ -knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, - const knot_dname_t *dname) +knot_zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *dname) { - int zone_labels = knot_dname_labels(dname, NULL); - if (db == NULL || zone_labels < 0) { + if (db == NULL || dname == NULL) { return NULL; } - /* Walk down the stack, from the most labels to least. */ - knot_zonedb_stack_t *sp = db->stack, *endp = db->stack + db->stack_height; - for (; sp != endp; ++sp) { - /* Inspect only zones with <= labels than zone_labels. */ - if (sp->labels > zone_labels) { - continue; - } - - /* Skip non-matched labels. */ - while (sp->labels < zone_labels) { - dname = knot_wire_next_label(dname, NULL); - --zone_labels; - } + /* 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; + } - /* Possible candidate, search the array. */ - int k = knot_zonedb_array_search(sp->array, sp->count, dname); - if (k > -1) { - return sp->array[k]; + /* Compare possible suffixes. */ + knot_zone_t *ret = NULL; + while (zone_labels > -1) { /* Include root label. */ + ret = knot_zonedb_find(db, dname); + if (ret != NULL) { + break; } + dname = knot_wire_next_label(dname, NULL); } - return NULL; + return ret; } /*----------------------------------------------------------------------------*/ @@ -275,7 +174,7 @@ knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, // Remove the contents from the zone, but keep the zone in the zonedb. - knot_zone_t *zone = knot_zonedb_find_zone(db, zone_name); + knot_zone_t *zone = knot_zonedb_find(db, zone_name); if (zone == NULL) { return NULL; } @@ -285,28 +184,20 @@ knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, /*----------------------------------------------------------------------------*/ -size_t knot_zonedb_zone_count(const knot_zonedb_t *db) +size_t knot_zonedb_size(const knot_zonedb_t *db) { - return db->count; + return db->hash->weight; } /*----------------------------------------------------------------------------*/ -const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db) +void knot_zonedb_free(knot_zonedb_t **db) { - if (db == NULL) { - return NULL; + if (db == NULL || *db == NULL) { + return; } - - return (const knot_zone_t **)db->array; -} -/*----------------------------------------------------------------------------*/ - -void knot_zonedb_free(knot_zonedb_t **db) -{ - free((*db)->array); - free(*db); + mp_delete((*db)->mm.ctx); *db = NULL; } @@ -314,9 +205,13 @@ void knot_zonedb_free(knot_zonedb_t **db) void knot_zonedb_deep_free(knot_zonedb_t **db) { - dbg_zonedb("Deleting zone db (%p).\n", *db); - for (unsigned i = 0; i < (*db)->count; ++i) { - delete_zone_from_db((*db)->array[i]); + /* Reindex for iteration. */ + knot_zonedb_build_index(*db); + knot_zonedb_iter_t it; + knot_zonedb_iter_begin(*db, &it); + while (!knot_zonedb_iter_finished(&it)) { + delete_zone_from_db(knot_zonedb_iter_val(&it)); + knot_zonedb_iter_next(&it); } knot_zonedb_free(db); diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h index 581cc300fdd89e52a0401d5d18549128649537f7..9d3e94e373552809186d654188b0d77470a77e13 100644 --- a/src/libknot/zone/zonedb.h +++ b/src/libknot/zone/zonedb.h @@ -33,41 +33,32 @@ #include "libknot/zone/zone.h" #include "libknot/zone/node.h" #include "libknot/dname.h" +#include "common/hhash.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). Linear run-length algorithms or worse should - * be avoided as well, as the number of zones may be large. - * - * Use of string-based algorithms for suffix search is viable, but would require - * transformation each time a name is searched. That again would be a - * constant cost even if the number of zones would be small. - * - * Zone database structure is a stack of zones grouped by label count in - * descending order (root label not counted), therefore first match is the longest. - * Each stack level is sorted for convenient binary search. - * example: - * {3 labels, 2 items} => [ 'a.b.c', 'b.b.c' ] - * {2 labels, 1 items} => [ 'x.z' ] - * {1 labels, 2 items} => [ 'y', 'w' ] - * - * Stack is built on top of the sorted array of zones for direct access and - * less memory requirements. + * 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 { - unsigned labels; - unsigned count; - knot_zone_t** array; -} knot_zonedb_stack_t; - -typedef struct { - unsigned count, reserved; - knot_zone_t **array; - unsigned stack_height; - knot_zonedb_stack_t stack[KNOT_DNAME_MAXLABELS]; + uint16_t maxlabels; + hhash_t *hash; + mm_ctx_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) + /*----------------------------------------------------------------------------*/ /*! @@ -76,7 +67,7 @@ typedef struct { * \return Pointer to the created zone database structure or NULL if an error * occured. */ -knot_zonedb_t *knot_zonedb_new(unsigned size); +knot_zonedb_t *knot_zonedb_new(uint32_t size); /*! * \brief Adds new zone to the database. @@ -87,7 +78,7 @@ knot_zonedb_t *knot_zonedb_new(unsigned size); * \retval KNOT_EOK * \retval KNOT_EZONEIN */ -int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone); +int knot_zonedb_insert(knot_zonedb_t *db, knot_zone_t *zone); /*! * \brief Removes the given zone from the database if it exists. @@ -98,18 +89,10 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone); * \retval KNOT_EOK * \retval KNOT_ENOZONE */ -knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db, - const knot_dname_t *zone_name); +int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name); /*! * \brief Build zone stack for faster lookup. - * - * Zone stack structure is described in the knot_zonedb_t struct. - * - * \param db Zone database. - * \retval KNOT_EOK - * \retval KNOT_ENOMEM - * \retval KNOT_EINVAL */ int knot_zonedb_build_index(knot_zonedb_t *db); @@ -122,8 +105,7 @@ int knot_zonedb_build_index(knot_zonedb_t *db); * \return Zone with \a zone_name being the owner of the zone apex or NULL if * not found. */ -knot_zone_t *knot_zonedb_find_zone(knot_zonedb_t *db, - const knot_dname_t *zone_name); +knot_zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name); /*! @@ -135,14 +117,12 @@ knot_zone_t *knot_zonedb_find_zone(knot_zonedb_t *db, * \retval Zone in which the domain name should be present or NULL if no such * zone is found. */ -knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, - const knot_dname_t *dname); +knot_zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *dname); knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, const knot_dname_t *zone_name); -size_t knot_zonedb_zone_count(const knot_zonedb_t *db); -const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db); +size_t knot_zonedb_size(const knot_zonedb_t *db); /*! * \brief Destroys and deallocates the zone database structure (but not the @@ -159,7 +139,6 @@ void knot_zonedb_free(knot_zonedb_t **db); */ void knot_zonedb_deep_free(knot_zonedb_t **db); -/*----------------------------------------------------------------------------*/ #endif /* _KNOT_ZONEDB_H_ */ diff --git a/tests/zonedb.c b/tests/zonedb.c index bdb6f57c96c9f56f27f9a53c0be3db6377168527..9ebfa3d570ed3d42885a64fd2e80573f7af4388b 100644 --- a/tests/zonedb.c +++ b/tests/zonedb.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) knot_dname_free(&dname); goto cleanup; } - if (knot_zonedb_add_zone(db, zones[i]) == KNOT_EOK) { + if (knot_zonedb_insert(db, zones[i]) == KNOT_EOK) { ++nr_passed; } else { diag("knot_zonedb_add_zone(%s) failed", zone_list[i]); @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) nr_passed = 0; for (unsigned i = 0; i < ZONE_COUNT; ++i) { dname = knot_dname_from_str(zone_list[i]); - if (knot_zonedb_find_zone(db, dname) == zones[i]) { + if (knot_zonedb_find(db, dname) == zones[i]) { ++nr_passed; } else { diag("knot_zonedb_find_zone(%s) failed", zone_list[i]); @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) strncat(buf, zone_list[i], strlen(zone_list[i])); } dname = knot_dname_from_str(buf); - if (knot_zonedb_find_zone_for_name(db, dname) == zones[i]) { + if (knot_zonedb_find_suffix(db, dname) == zones[i]) { ++nr_passed; } else { diag("knot_zonedb_find_zone(%s) failed", buf); @@ -101,8 +101,7 @@ int main(int argc, char *argv[]) nr_passed = 0; for (unsigned i = 0; i < ZONE_COUNT; ++i) { dname = knot_dname_from_str(zone_list[i]); - zone = knot_zonedb_remove_zone(db, dname); - if (zone == zones[i]) { + if (knot_zonedb_del(db, dname) == KNOT_EOK) { knot_zone_free(&zone); ++nr_passed; } else {