Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (11)
Showing with 347 additions and 1018 deletions
......@@ -8,17 +8,18 @@
#include "contrib/cleanup.h"
#include "daemon/network.h"
#include "daemon/tls.h"
#include "lib/utils.h"
#include <stdlib.h>
#define PROXY_DATA_STRLEN (INET6_ADDRSTRLEN + 1 + 3 + 1)
/** Table and next index on top of stack -> append entries for given endpoint_array_t. */
static int net_list_add(const char *key, void *val, void *ext)
static int net_list_add(const char *b_key, uint32_t key_len, trie_val_t *val, void *ext)
{
endpoint_array_t *ep_array = *val;
lua_State *L = (lua_State *)ext;
lua_Integer i = lua_tointeger(L, -1);
endpoint_array_t *ep_array = val;
for (int j = 0; j < ep_array->len; ++j) {
struct endpoint *ep = &ep_array->at[j];
lua_newtable(L); // connection tuple
......@@ -57,7 +58,15 @@ static int net_list_add(const char *key, void *val, void *ext)
}
lua_setfield(L, -2, "family");
lua_pushstring(L, key);
const char *ip_str_const = network_endpoint_key_str((struct endpoint_key *) b_key);
kr_require(ip_str_const);
auto_free char *ip_str = strdup(ip_str_const);
kr_require(ip_str);
char *hm = strchr(ip_str, '#');
if (hm) /* Omit port */
*hm = '\0';
lua_pushstring(L, ip_str);
if (ep->family == AF_INET || ep->family == AF_INET6) {
lua_setfield(L, -2, "ip");
lua_pushboolean(L, ep->flags.freebind);
......@@ -101,7 +110,7 @@ static int net_list(lua_State *L)
{
lua_newtable(L);
lua_pushinteger(L, 1);
map_walk(&the_worker->engine->net.endpoints, net_list_add, L);
trie_apply_with_key(the_worker->engine->net.endpoints, net_list_add, L);
lua_pop(L, 1);
return 1;
}
......
......@@ -94,10 +94,6 @@ struct knot_pkt {
knot_mm_t mm;
knot_compr_t compr;
};
typedef struct {
void *root;
struct knot_mm *pool;
} map_t;
typedef struct trie trie_t;
struct kr_qflags {
_Bool NO_MINIMIZE : 1;
......
......@@ -94,10 +94,6 @@ struct knot_pkt {
knot_mm_t mm;
knot_compr_t compr;
};
typedef struct {
void *root;
struct knot_mm *pool;
} map_t;
typedef struct trie trie_t;
struct kr_qflags {
_Bool NO_MINIMIZE : 1;
......
......@@ -111,8 +111,6 @@ ${CDEFS} ${LIBKRES} types <<-EOF
knot_pktsection_t
knot_compr_t
struct knot_pkt
# lib/generic/
map_t
#trie_t inside is private to libknot
typedef trie_t
# libkres
......
......@@ -4,10 +4,12 @@
#include "daemon/network.h"
#include "contrib/cleanup.h"
#include "daemon/bindings/impl.h"
#include "daemon/io.h"
#include "daemon/tls.h"
#include "daemon/worker.h"
#include "lib/utils.h"
#if ENABLE_XDP
#include <libknot/xdp/eth.h>
......@@ -18,11 +20,48 @@
#include <sys/un.h>
#include <unistd.h>
/** Determines the type of `struct endpoint_key`. */
enum endpoint_key_type
{
ENDPOINT_KEY_SOCKADDR = 1,
ENDPOINT_KEY_IFNAME = 2,
};
/** Used as a key in the `struct network::endpoints` trie. */
struct endpoint_key {
enum endpoint_key_type type;
char data[];
};
struct __attribute__((packed)) endpoint_key_sockaddr {
enum endpoint_key_type type;
struct kr_sockaddr_key_storage sa_key;
};
struct __attribute__((packed)) endpoint_key_ifname {
enum endpoint_key_type type;
char ifname[128];
};
/** Used for reserving enough storage for `endpoint_key`. */
struct endpoint_key_storage {
union {
enum endpoint_key_type type;
struct endpoint_key_sockaddr sa;
struct endpoint_key_ifname ifname;
char bytes[1]; /* for easier casting */
};
};
static_assert(_Alignof(struct endpoint_key) <= 4, "endpoint_key must be aligned to <=4");
static_assert(_Alignof(struct endpoint_key_sockaddr) <= 4, "endpoint_key must be aligned to <=4");
static_assert(_Alignof(struct endpoint_key_ifname) <= 4, "endpoint_key must be aligned to <=4");
void network_init(struct network *net, uv_loop_t *loop, int tcp_backlog)
{
if (net != NULL) {
net->loop = loop;
net->endpoints = map_make(NULL);
net->endpoints = trie_create(NULL);
net->endpoint_kinds = trie_create(NULL);
net->proxy_all4 = false;
net->proxy_all6 = false;
......@@ -77,24 +116,29 @@ static int endpoint_open_lua_cb(struct network *net, struct endpoint *ep,
return kr_ok();
}
static int engage_endpoint_array(const char *key, void *endpoints, void *net)
static int engage_endpoint_array(const char *b_key, uint32_t key_len, trie_val_t *val, void *net)
{
endpoint_array_t *eps = (endpoint_array_t *)endpoints;
const char *log_addr = network_endpoint_key_str((struct endpoint_key *) b_key);
if (!log_addr)
log_addr = "[unknown]";
endpoint_array_t *eps = *val;
for (int i = 0; i < eps->len; ++i) {
struct endpoint *ep = &eps->at[i];
const bool match = !ep->engaged && ep->flags.kind;
if (!match) continue;
int ret = endpoint_open_lua_cb(net, ep, key);
int ret = endpoint_open_lua_cb(net, ep, log_addr);
if (ret) return ret;
}
return 0;
}
int network_engage_endpoints(struct network *net)
{
if (net->missing_kind_is_error)
return kr_ok(); /* maybe weird, but let's make it idempotent */
net->missing_kind_is_error = true;
int ret = map_walk(&net->endpoints, engage_endpoint_array, net);
int ret = trie_apply_with_key(net->endpoints, engage_endpoint_array, net);
if (ret) {
net->missing_kind_is_error = false; /* avoid the same errors when closing */
return ret;
......@@ -102,6 +146,25 @@ int network_engage_endpoints(struct network *net)
return kr_ok();
}
const char *network_endpoint_key_str(const struct endpoint_key *key)
{
switch (key->type)
{
case ENDPOINT_KEY_SOCKADDR:;
const struct endpoint_key_sockaddr *sa_key =
(struct endpoint_key_sockaddr *) key;
struct sockaddr_storage sa_storage;
struct sockaddr *sa = kr_sockaddr_from_key(&sa_storage, (const char *) &sa_key->sa_key);
return kr_straddr(sa);
case ENDPOINT_KEY_IFNAME:;
const struct endpoint_key_ifname *if_key =
(struct endpoint_key_ifname *) key;
return if_key->ifname;
default:
kr_assert(false);
return NULL;
}
}
/** Notify the registered function about endpoint about to be closed. */
static void endpoint_close_lua_cb(struct network *net, struct endpoint *ep)
......@@ -173,19 +236,19 @@ static void endpoint_close(struct network *net, struct endpoint *ep, bool force)
}
}
/** Endpoint visitor (see @file map.h) */
static int close_key(const char *key, void *val, void *net)
/** Endpoint visitor (see @file trie.h) */
static int close_key(trie_val_t *val, void* net)
{
endpoint_array_t *ep_array = val;
endpoint_array_t *ep_array = *val;
for (int i = 0; i < ep_array->len; ++i) {
endpoint_close(net, &ep_array->at[i], true);
}
return 0;
}
static int free_key(const char *key, void *val, void *ext)
static int free_key(trie_val_t *val, void* ext)
{
endpoint_array_t *ep_array = val;
endpoint_array_t *ep_array = *val;
array_clear(*ep_array);
free(ep_array);
return kr_ok();
......@@ -201,9 +264,9 @@ int kind_unregister(trie_val_t *tv, void *L)
void network_close_force(struct network *net)
{
if (net != NULL) {
map_walk(&net->endpoints, close_key, net);
map_walk(&net->endpoints, free_key, 0);
map_clear(&net->endpoints);
trie_apply(net->endpoints, close_key, net);
trie_apply(net->endpoints, free_key, NULL);
trie_clear(net->endpoints);
}
}
......@@ -224,6 +287,7 @@ void network_deinit(struct network *net)
network_close_force(net);
trie_apply(net->endpoint_kinds, kind_unregister, the_worker->engine->L);
trie_free(net->endpoint_kinds);
trie_free(net->endpoints);
network_proxy_free_addr_data(net->proxy_addrs4);
trie_free(net->proxy_addrs4);
network_proxy_free_addr_data(net->proxy_addrs6);
......@@ -238,21 +302,44 @@ void network_deinit(struct network *net)
}
}
static ssize_t endpoint_key_create(struct endpoint_key_storage *dst,
const char *addr_str,
const struct sockaddr *sa)
{
memset(dst, 0, sizeof(*dst));
if (sa) {
struct endpoint_key_sockaddr *key = &dst->sa;
key->type = ENDPOINT_KEY_SOCKADDR;
ssize_t keylen = kr_sockaddr_key(&key->sa_key, sa);
if (keylen < 0)
return keylen;
return sizeof(struct endpoint_key) + keylen;
} else {
struct endpoint_key_ifname *key = &dst->ifname;
key->type = ENDPOINT_KEY_IFNAME;
strncpy(key->ifname, addr_str, sizeof(key->ifname) - 1);
return sizeof(struct endpoint_key) + strnlen(key->ifname, sizeof(key->ifname));
}
}
/** Fetch or create endpoint array and insert endpoint (shallow memcpy). */
static int insert_endpoint(struct network *net, const char *addr, struct endpoint *ep)
static int insert_endpoint(struct network *net, const char *addr_str,
const struct sockaddr *addr, struct endpoint *ep)
{
/* Fetch or insert address into map */
endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
if (ep_array == NULL) {
struct endpoint_key_storage key;
ssize_t keylen = endpoint_key_create(&key, addr_str, addr);
if (keylen < 0)
return keylen;
trie_val_t *val = trie_get_ins(net->endpoints, key.bytes, keylen);
endpoint_array_t *ep_array;
if (*val) {
ep_array = *val;
} else {
ep_array = malloc(sizeof(*ep_array));
if (ep_array == NULL) {
return kr_error(ENOMEM);
}
if (map_set(&net->endpoints, addr, ep_array) != 0) {
free(ep_array);
return kr_error(ENOMEM);
}
kr_require(ep_array);
array_init(*ep_array);
*val = ep_array;
}
if (array_reserve(*ep_array, ep_array->len + 1)) {
......@@ -269,17 +356,16 @@ static int open_endpoint(struct network *net, const char *addr_str,
{
const bool is_control = ep->flags.kind && strcmp(ep->flags.kind, "control") == 0;
const bool is_xdp = ep->family == AF_XDP;
bool ok = is_xdp
? sa == NULL && ep->fd == -1 && ep->nic_queue >= 0
&& ep->flags.sock_type == SOCK_DGRAM && !ep->flags.tls
: (sa != NULL) != (ep->fd != -1);
bool ok = (!is_xdp)
|| (sa == NULL && ep->fd == -1 && ep->nic_queue >= 0
&& ep->flags.sock_type == SOCK_DGRAM && !ep->flags.tls);
if (kr_fails_assert(ok))
return kr_error(EINVAL);
if (ep->handle) {
return kr_error(EEXIST);
}
if (sa) {
if (sa && ep->fd == -1) {
if (sa->sa_family == AF_UNIX) {
struct sockaddr_un *sun = (struct sockaddr_un*)sa;
char *dirc = strdup(sun->sun_path);
......@@ -363,16 +449,24 @@ static int open_endpoint(struct network *net, const char *addr_str,
* Beware that there might be multiple matches, though that's not common.
* The matching isn't really precise in the sense that it might not find
* and endpoint that would *collide* the passed one. */
static struct endpoint * endpoint_get(struct network *net, const char *addr,
uint16_t port, endpoint_flags_t flags)
static struct endpoint * endpoint_get(struct network *net,
const char *addr_str,
const struct sockaddr *sa,
endpoint_flags_t flags)
{
endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
if (!ep_array) {
struct endpoint_key_storage key;
ssize_t keylen = endpoint_key_create(&key, addr_str, sa);
if (keylen < 0)
return NULL;
}
trie_val_t *val = trie_get_try(net->endpoints, key.bytes, keylen);
if (!val)
return NULL;
endpoint_array_t *ep_array = *val;
uint16_t port = kr_inaddr_port(sa);
for (int i = 0; i < ep_array->len; ++i) {
struct endpoint *ep = &ep_array->at[i];
if (ep->port == port && endpoint_flags_eq(ep->flags, flags)) {
if ((flags.xdp || ep->port == port) && endpoint_flags_eq(ep->flags, flags)) {
return ep;
}
}
......@@ -383,11 +477,11 @@ static struct endpoint * endpoint_get(struct network *net, const char *addr,
* \note in XDP case addr_str is interface name
* \note ownership of ep.flags.* is taken on success. */
static int create_endpoint(struct network *net, const char *addr_str,
struct endpoint *ep, const struct sockaddr *sa)
struct endpoint *ep, const struct sockaddr *sa)
{
int ret = open_endpoint(net, addr_str, ep, sa);
if (ret == 0) {
ret = insert_endpoint(net, addr_str, ep);
ret = insert_endpoint(net, addr_str, sa, ep);
}
if (ret != 0 && ep->handle) {
endpoint_close(net, ep, false);
......@@ -448,7 +542,7 @@ int network_listen_fd(struct network *net, int fd, endpoint_flags_t flags)
/* always create endpoint for supervisor supplied fd
* even if addr+port is not unique */
return create_endpoint(net, addr_str, &ep, NULL);
return create_endpoint(net, addr_str, &ep, (struct sockaddr *) &ss);
}
/** Try selecting XDP queue automatically. */
......@@ -501,7 +595,7 @@ int network_listen(struct network *net, const char *addr, uint16_t port,
}
// XDP: if addr failed to parse as address, we assume it's an interface name.
if (endpoint_get(net, addr, port, flags)) {
if (endpoint_get(net, addr, sa, flags)) {
return kr_error(EADDRINUSE); // Already listening
}
......@@ -613,13 +707,10 @@ void network_proxy_reset(struct network *net)
trie_clear(net->proxy_addrs6);
}
int network_close(struct network *net, const char *addr, int port)
static int endpoints_close(struct network *net,
struct endpoint_key_storage *key, ssize_t keylen,
endpoint_array_t *ep_array, int port)
{
endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
if (!ep_array) {
return kr_error(ENOENT);
}
size_t i = 0;
bool matched = false; /*< at least one match */
while (i < ep_array->len) {
......@@ -637,14 +728,108 @@ int network_close(struct network *net, const char *addr, int port)
return kr_error(ENOENT);
}
return kr_ok();
}
static bool endpoint_key_addr_matches(struct endpoint_key_storage *key_a,
struct endpoint_key_storage *key_b)
{
if (key_a->type != key_b->type)
return false;
if (key_a->type == ENDPOINT_KEY_IFNAME)
return strncmp(key_a->ifname.ifname,
key_b->ifname.ifname,
sizeof(key_a->ifname.ifname)) == 0;
if (key_a->type == ENDPOINT_KEY_SOCKADDR) {
return kr_sockaddr_key_same_addr(
key_a->sa.sa_key.bytes, key_b->sa.sa_key.bytes);
}
kr_assert(false);
return kr_error(EINVAL);
}
struct endpoint_key_with_len {
struct endpoint_key_storage key;
size_t keylen;
};
typedef array_t(struct endpoint_key_with_len) endpoint_key_array_t;
struct endpoint_close_wildcard_context {
struct network *net;
struct endpoint_key_storage *match_key;
endpoint_key_array_t del;
int ret;
};
static int endpoints_close_wildcard(const char *s_key, uint32_t keylen, trie_val_t *val, void *baton)
{
struct endpoint_close_wildcard_context *ctx = baton;
struct endpoint_key_storage *key = (struct endpoint_key_storage *)s_key;
if (!endpoint_key_addr_matches(key, ctx->match_key))
return kr_ok();
endpoint_array_t *ep_array = *val;
int ret = endpoints_close(ctx->net, key, keylen, ep_array, -1);
if (ret)
ctx->ret = ret;
if (ep_array->len == 0) {
struct endpoint_key_with_len to_del = {
.key = *key,
.keylen = keylen
};
array_push(ctx->del, to_del);
}
return kr_ok();
}
int network_close(struct network *net, const char *addr_str, int port)
{
auto_free struct sockaddr *addr = kr_straddr_socket(addr_str, port, NULL);
struct endpoint_key_storage key;
ssize_t keylen = endpoint_key_create(&key, addr_str, addr);
if (keylen < 0)
return keylen;
if (port < 0) {
struct endpoint_close_wildcard_context ctx = {
.net = net,
.match_key = &key
};
array_init(ctx.del);
trie_apply_with_key(net->endpoints, endpoints_close_wildcard, &ctx);
for (size_t i = 0; i < ctx.del.len; i++) {
trie_val_t val;
trie_del(net->endpoints,
ctx.del.at[i].key.bytes, ctx.del.at[i].keylen,
&val);
if (val) {
array_clear(*(endpoint_array_t *) val);
free(val);
}
}
return ctx.ret;
}
trie_val_t *val = trie_get_try(net->endpoints, key.bytes, keylen);
if (!val)
return kr_error(ENOENT);
endpoint_array_t *ep_array = *val;
int ret = endpoints_close(net, &key, keylen, ep_array, port);
/* Collapse key if it has no endpoint. */
if (ep_array->len == 0) {
array_clear(*ep_array);
free(ep_array);
map_del(&net->endpoints, addr);
trie_del(net->endpoints, key.bytes, keylen, NULL);
}
return kr_ok();
return ret;
}
void network_new_hostname(struct network *net, struct engine *engine)
......@@ -664,10 +849,10 @@ void network_new_hostname(struct network *net, struct engine *engine)
}
#ifdef SO_ATTACH_BPF
static int set_bpf_cb(const char *key, void *val, void *ext)
static int set_bpf_cb(trie_val_t *val, void *ctx)
{
endpoint_array_t *endpoints = (endpoint_array_t *)val;
int *bpffd = (int *)ext;
endpoint_array_t *endpoints = *val;
int *bpffd = (int *)ctx;
if (kr_fails_assert(endpoints && bpffd))
return kr_error(EINVAL);
......@@ -689,7 +874,7 @@ static int set_bpf_cb(const char *key, void *val, void *ext)
int network_set_bpf(struct network *net, int bpf_fd)
{
#ifdef SO_ATTACH_BPF
if (map_walk(&net->endpoints, set_bpf_cb, &bpf_fd) != 0) {
if (trie_apply(net->endpoints, set_bpf_cb, &bpf_fd) != 0) {
/* set_bpf_cb() has returned error. */
network_clear_bpf(net);
return 0;
......@@ -704,9 +889,9 @@ int network_set_bpf(struct network *net, int bpf_fd)
}
#ifdef SO_DETACH_BPF
static int clear_bpf_cb(const char *key, void *val, void *ext)
static int clear_bpf_cb(trie_val_t *val, void *ctx)
{
endpoint_array_t *endpoints = (endpoint_array_t *)val;
endpoint_array_t *endpoints = *val;
if (kr_fails_assert(endpoints))
return kr_error(EINVAL);
......@@ -730,7 +915,7 @@ static int clear_bpf_cb(const char *key, void *val, void *ext)
void network_clear_bpf(struct network *net)
{
#ifdef SO_DETACH_BPF
map_walk(&net->endpoints, clear_bpf_cb, NULL);
trie_apply(net->endpoints, clear_bpf_cb, NULL);
#else
kr_log_error(NETWORK, "SO_DETACH_BPF socket option doesn't supported\n");
(void)net;
......
......@@ -7,7 +7,6 @@
#include "daemon/tls.h"
#include "lib/generic/array.h"
#include "lib/generic/map.h"
#include "lib/generic/trie.h"
#include <uv.h>
......@@ -31,6 +30,8 @@ typedef struct {
const char *kind; /**< tag for other types: "control" or module-handled kinds */
} endpoint_flags_t;
struct endpoint_key;
static inline bool endpoint_flags_eq(endpoint_flags_t f1, endpoint_flags_t f2)
{
if (f1.sock_type != f2.sock_type)
......@@ -78,9 +79,8 @@ struct network {
uv_loop_t *loop;
/** Map: address string -> endpoint_array_t.
* \note even same address-port-flags tuples may appear.
* TODO: trie_t, keyed on *binary* address-port pair. */
map_t endpoints;
* \note even same address-port-flags tuples may appear. */
trie_t *endpoints;
/** Registry of callbacks for special endpoint kinds (for opening/closing).
* Map: kind (lowercased) -> lua function ID converted to void *
......@@ -150,6 +150,11 @@ void network_close_force(struct network *net);
* This only does anything with struct endpoint::flags.kind != NULL. */
int network_engage_endpoints(struct network *net);
/** Returns a string representation of the specified endpoint key.
*
* The result points into key or is on static storage like for kr_straddr() */
const char *network_endpoint_key_str(const struct endpoint_key *key);
int network_set_tls_cert(struct network *net, const char *cert);
int network_set_tls_key(struct network *net, const char *key);
void network_new_hostname(struct network *net, struct engine *engine);
......
......@@ -47,7 +47,6 @@
#include "daemon/worker.h"
#include "lib/dnssec/ta.h"
#include "lib/dnssec.h"
#include "lib/generic/map.h"
#include "lib/generic/array.h"
#include "lib/generic/trie.h"
#include "lib/utils.h"
......
......@@ -55,13 +55,6 @@ Files: contrib/ccan/json/*
Copyright: 2011 Joey Adams
License: Expat
Files: lib/generic/map.c lib/generic/map.h
Copyright: Dan Bernstein
Jonas Gehring
Adam Langley
Marek Vavrusa
License: public-domain
Files: modules/policy/lua-aho-corasick/*
Copyright: 2013 CloudFlare, Inc.
License: BSD-3-CloudFlare
......
......@@ -10,8 +10,6 @@ as long as it comes with a test case in `tests/test_generics.c`.
* array_ - a set of simple macros to make working with dynamic arrays easier.
* queue_ - a FIFO + LIFO queue.
* map_ - a `Crit-bit tree`_ key-value map implementation (public domain) that comes with tests.
* set_ - set abstraction implemented on top of ``map`` (unused now).
* pack_ - length-prefixed list of objects (i.e. array-list).
* lru_ - LRU-like hash table
* trie_ - a trie-based key-value map, taken from knot-dns
......@@ -28,18 +26,6 @@ queue
.. doxygenfile:: queue.h
:project: libkres
map
~~~
.. doxygenfile:: map.h
:project: libkres
set
~~~
.. doxygenfile:: set.h
:project: libkres
pack
~~~~
......
/*
* critbit89 - A crit-bit tree implementation for strings in C89
* Written by Jonas Gehring <jonas@jgehring.net>
* Implemented key-value storing by Marek Vavrusa <marek.vavrusa@nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* The code makes the assumption that malloc returns pointers aligned at at
* least a two-byte boundary. Since the C standard requires that malloc return
* pointers that can store any type, there are no commonly-used toolchains for
* which this assumption is false.
*
* See https://github.com/agl/critbit/blob/master/critbit.pdf for reference.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "map.h"
#include "lib/utils.h"
/* Exports */
#if defined _WIN32 || defined __CYGWIN__
#define EXPORT __attribute__ ((dllexport))
#else
#define EXPORT __attribute__ ((visibility ("default")))
#endif
#ifdef _MSC_VER /* MSVC */
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
#ifdef _WIN64
typedef signed __int64 intptr_t;
#else
typedef _W64 signed int intptr_t;
#endif
#else /* Not MSVC */
#include <stdint.h>
#endif
typedef struct {
void* value;
uint8_t key[];
} cb_data_t;
typedef struct {
void *child[2];
uint32_t byte;
uint8_t otherbits;
} cb_node_t;
/* Return true if ptr is internal node. */
static inline int ref_is_internal(const uint8_t *p)
{
return 1 & (intptr_t)p;
}
/* Get internal node. */
static inline cb_node_t *ref_get_internal(uint8_t *p)
{
return (cb_node_t *)(p - 1);
}
/* Static helper functions */
static void cbt_traverse_delete(map_t *map, void *top)
{
uint8_t *p = top;
if (ref_is_internal(p)) {
cb_node_t *q = ref_get_internal(p);
cbt_traverse_delete(map, q->child[0]);
cbt_traverse_delete(map, q->child[1]);
mm_free(map->pool, q);
} else {
mm_free(map->pool, p);
}
}
static int cbt_traverse_prefixed(void *top,
int (*callback)(const char *, void *, void *), void *baton)
{
uint8_t *p = top;
cb_data_t *x = (cb_data_t *)top;
if (ref_is_internal(p)) {
cb_node_t *q = ref_get_internal(p);
int ret = 0;
ret = cbt_traverse_prefixed(q->child[0], callback, baton);
if (ret != 0) {
return ret;
}
ret = cbt_traverse_prefixed(q->child[1], callback, baton);
if (ret != 0) {
return ret;
}
return 0;
}
return (callback)((const char *)x->key, x->value, baton);
}
static cb_data_t *cbt_make_data(map_t *map, const uint8_t *str, size_t len, void *value)
{
cb_data_t *x = mm_alloc(map->pool, sizeof(cb_data_t) + len);
if (x != NULL) {
x->value = value;
memcpy(x->key, str, len);
}
return x;
}
/*! Like map_contains, but also set the value, if passed and found. */
static int cbt_get(map_t *map, const char *str, void **value)
{
const uint8_t *ubytes = (void *)str;
const size_t ulen = strlen(str);
uint8_t *p = map->root;
cb_data_t *x = NULL;
if (p == NULL) {
return 0;
}
while (ref_is_internal(p)) {
cb_node_t *q = ref_get_internal(p);
uint8_t c = 0;
int direction;
if (q->byte < ulen) {
c = ubytes[q->byte];
}
direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
}
x = (cb_data_t *)p;
if (strcmp(str, (const char *)x->key) == 0) {
if (value != NULL) {
*value = x->value;
}
return 1;
}
return 0;
}
/*! Returns non-zero if map contains str */
EXPORT int map_contains(map_t *map, const char *str)
{
return cbt_get(map, str, NULL);
}
EXPORT void *map_get(map_t *map, const char *str)
{
void *v = NULL;
cbt_get(map, str, &v);
return v;
}
EXPORT int map_set(map_t *map, const char *str, void *val)
{
const uint8_t *const ubytes = (void *)str;
const size_t ulen = strlen(str);
uint8_t *p = map->root;
uint8_t c = 0, *x = NULL;
uint32_t newbyte = 0;
uint32_t newotherbits = 0;
int direction = 0, newdirection = 0;
cb_node_t *newnode = NULL;
cb_data_t *data = NULL;
void **wherep = NULL;
if (p == NULL) {
map->root = cbt_make_data(map, (const uint8_t *)str, ulen + 1, val);
if (map->root == NULL) {
return ENOMEM;
}
return 0;
}
while (ref_is_internal(p)) {
cb_node_t *q = ref_get_internal(p);
c = 0;
if (q->byte < ulen) {
c = ubytes[q->byte];
}
direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
}
data = (cb_data_t *)p;
for (newbyte = 0; newbyte < ulen; ++newbyte) {
if (data->key[newbyte] != ubytes[newbyte]) {
newotherbits = data->key[newbyte] ^ ubytes[newbyte];
goto different_byte_found;
}
}
if (data->key[newbyte] != 0) {
newotherbits = data->key[newbyte];
goto different_byte_found;
}
data->value = val;
return 1;
different_byte_found:
newotherbits |= newotherbits >> 1;
newotherbits |= newotherbits >> 2;
newotherbits |= newotherbits >> 4;
newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
c = data->key[newbyte];
newdirection = (1 + (newotherbits | c)) >> 8;
newnode = mm_alloc(map->pool, sizeof(cb_node_t));
if (newnode == NULL) {
return ENOMEM;
}
x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, val);
if (x == NULL) {
mm_free(map->pool, newnode);
return ENOMEM;
}
newnode->byte = newbyte;
newnode->otherbits = newotherbits;
newnode->child[1 - newdirection] = x;
/* Insert into map */
wherep = &map->root;
for (;;) {
cb_node_t *q;
p = *wherep;
if (!ref_is_internal(p)) {
break;
}
q = ref_get_internal(p);
if (q->byte > newbyte) {
break;
}
if (q->byte == newbyte && q->otherbits > newotherbits) {
break;
}
c = 0;
if (q->byte < ulen) {
c = ubytes[q->byte];
}
direction = (1 + (q->otherbits | c)) >> 8;
wherep = q->child + direction;
}
newnode->child[newdirection] = *wherep;
*wherep = (void *)(1 + (char *)newnode);
return 0;
}
/*! Deletes str from the map, returns 0 on success */
EXPORT int map_del(map_t *map, const char *str)
{
const uint8_t *ubytes = (void *)str;
const size_t ulen = strlen(str);
uint8_t *p = map->root;
void **wherep = NULL, **whereq = NULL;
cb_node_t *q = NULL;
cb_data_t *data = NULL;
int direction = 0;
if (map->root == NULL) {
return 1;
}
wherep = &map->root;
while (ref_is_internal(p)) {
uint8_t c = 0;
whereq = wherep;
q = ref_get_internal(p);
if (q->byte < ulen) {
c = ubytes[q->byte];
}
direction = (1 + (q->otherbits | c)) >> 8;
wherep = q->child + direction;
p = *wherep;
}
data = (cb_data_t *)p;
if (strcmp(str, (const char *)data->key) != 0) {
return 1;
}
mm_free(map->pool, p);
if (!whereq) {
map->root = NULL;
return 0;
}
*whereq = q->child[1 - direction];
mm_free(map->pool, q);
return 0;
}
/*! Clears the given map */
EXPORT void map_clear(map_t *map)
{
if (map->root) {
cbt_traverse_delete(map, map->root);
}
map->root = NULL;
}
/*! Calls callback for all strings in map with the given prefix */
EXPORT int map_walk_prefixed(map_t *map, const char *prefix,
int (*callback)(const char *, void *, void *), void *baton)
{
if (!map) {
return 0;
}
const uint8_t *ubytes = (void *)prefix;
const size_t ulen = strlen(prefix);
uint8_t *p = map->root;
uint8_t *top = p;
cb_data_t *data = NULL;
if (p == NULL) {
return 0;
}
while (ref_is_internal(p)) {
cb_node_t *q = ref_get_internal(p);
uint8_t c = 0;
int direction;
if (q->byte < ulen) {
c = ubytes[q->byte];
}
direction = (1 + (q->otherbits | c)) >> 8;
p = q->child[direction];
if (q->byte < ulen) {
top = p;
}
}
data = (cb_data_t *)p;
if (strlen((const char *)data->key) < ulen || memcmp(data->key, prefix, ulen) != 0) {
return 0; /* No strings match */
}
return cbt_traverse_prefixed(top, callback, baton);
}
/*
* critbit89 - A crit-bit map implementation for strings in C89
* Written by Jonas Gehring <jonas@jgehring.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/**
* @file map.h
* @brief A Crit-bit tree key-value map implementation.
*
* @warning If the user provides a custom allocator, it must return addresses aligned to 2B boundary.
*
* # Example usage:
*
* @code{.c}
* map_t map = map_make(NULL);
*
* // Custom allocator (optional)
* map.malloc = &mymalloc;
* map.baton = &mymalloc_context;
*
* // Insert k-v pairs
* int values = { 42, 53, 64 };
* if (map_set(&map, "princess", &values[0]) != 0 ||
* map_set(&map, "prince", &values[1]) != 0 ||
* map_set(&map, "leia", &values[2]) != 0) {
* fail();
* }
*
* // Test membership
* if (map_contains(&map, "leia")) {
* success();
* }
*
* // Prefix search
* int i = 0;
* int count(const char *k, void *v, void *ext) { (*(int *)ext)++; return 0; }
* if (map_walk_prefixed(map, "princ", count, &i) == 0) {
* printf("%d matches\n", i);
* }
*
* // Delete
* if (map_del(&map, "badkey") != 0) {
* fail(); // No such key
* }
*
* // Clear the map
* map_clear(&map);
* @endcode
*
* \addtogroup generics
* @{
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct knot_mm; /* avoid the unnecessary include */
/** Main data structure */
typedef struct {
void *root;
struct knot_mm *pool;
} map_t;
/** Creates an new empty critbit map. Pass NULL for malloc+free. */
static inline map_t map_make(struct knot_mm *pool)
{
return (map_t){ .root = NULL, .pool = pool };
}
/** Returns non-zero if map contains str */
int map_contains(map_t *map, const char *str);
/** Returns value if map contains str. Note: NULL may mean two different things. */
void *map_get(map_t *map, const char *str);
/** Inserts str into map. Returns 0 if new, 1 if replaced, or ENOMEM. */
int map_set(map_t *map, const char *str, void *val);
/** Deletes str from the map, returns 0 on success */
int map_del(map_t *map, const char *str);
/** Clears the given map */
void map_clear(map_t *map);
/**
* Calls callback for all strings in map
* See @fn map_walk_prefixed() for documentation on parameters.
*/
#define map_walk(map, callback, baton) \
map_walk_prefixed((map), "", (callback), (baton))
/**
* Calls callback for all strings in map with the given prefix.
* Returns value immediately if a callback returns nonzero.
*
* @param map
* @param prefix required string prefix (empty => all strings)
* @param callback callback parameters are (key, value, baton)
* @param baton passed uservalue
*/
int map_walk_prefixed(map_t *map, const char *prefix,
int (*callback)(const char *, void *, void *), void *baton);
#ifdef __cplusplus
}
#endif
/** @} */
SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: map
DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-d9b4db4c-062f-4add-89b6-f603224f5a2c
PackageName: critbit89
PackageDownloadLocation: git+https://github.com/jgehring/critbit89.git@4f7e1d2a5f4794e0d08cb408346973fb1e39489c#critbit.c
PackageOriginator: Person: Jonas Gehring (jonas@jgehring.net)
PackageLicenseDeclared: CC0-1.0
/* Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/**
* @file set.h
* @brief A set abstraction implemented on top of map.
*
* @note The API is based on map.h, see it for more examples.
*
* # Example usage:
*
* @code{.c}
* set_t set = set_make(NULL);
*
* // Insert keys
* if (set_add(&set, "princess") != 0 ||
* set_add(&set, "prince") != 0 ||
* set_add(&set, "leia") != 0) {
* fail();
* }
*
* // Test membership
* if (set_contains(&set, "leia")) {
* success();
* }
*
* // Prefix search
* int i = 0;
* int count(const char *s, void *n) { (*(int *)n)++; return 0; }
* if (set_walk_prefixed(set, "princ", count, &i) == 0) {
* printf("%d matches\n", i);
* }
*
* // Delete
* if (set_del(&set, "badkey") != 0) {
* fail(); // No such key
* }
*
* // Clear the set
* set_clear(&set);
* @endcode
*
* \addtogroup generics
* @{
*/
#pragma once
#include <stddef.h>
#include "map.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef map_t set_t;
typedef int (set_walk_cb)(const char *, void *);
/*! Creates an new, empty critbit set */
#define set_make \
map_make
/*! Returns non-zero if set contains str */
#define set_contains(set, str) \
map_contains((set), (str))
/*! Inserts str into set. Returns 0 if new, 1 if already present, or ENOMEM. */
#define set_add(set, str) \
map_set((set), (str), (void *)1)
/*! Deletes str from the set, returns 0 on success */
#define set_del(set, str) \
map_del((set), (str))
/*! Clears the given set */
#define set_clear(set) \
map_clear(set)
/*! Calls callback for all strings in map */
#define set_walk(set, callback, baton) \
map_walk_prefixed((set), "", (callback), (baton))
/*! Calls callback for all strings in set with the given prefix */
#define set_walk_prefixed(set, prefix, callback, baton) \
map_walk_prefixed((set), (prefix), (callback), (baton))
#ifdef __cplusplus
}
#endif
/** @} */
/* Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tests/unit/test.h"
#include "lib/generic/map.h"
/*
* Sample dictionary
*/
static const char *dict[] = {
"catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
"alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
"seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
"physiologically", "economizer", "forcepslike", "betrumpet",
"Danization", "broadthroat", "randir", "usherette", "nephropyosis",
"hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
"siphonognathous", "indoor", "featheriness", "forwardation",
"archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
"unpragmatical", "childless", "transumpt", "productive",
"thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
"pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
"smoothbored", "widower", "sivathere", "pendle", "saltation",
"autopelagic", "campfight", "unexplained", "Macrorhamphosus",
"absconsa", "counterflory", "interdependent", "triact", "reconcentration",
"oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
"pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
"Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
"cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
"bacillogenic", "hamartite", "polytheistical", "unescapableness",
"Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
"perishless", "cupidity", "semilichen", "gadge", "detrimental",
"misencourage", "toparchia", "lurchingly", "apocatastasis"
};
/* Insertions */
static void test_insert(void **state)
{
map_t *tree = *state;
int dict_size = sizeof(dict) / sizeof(const char *);
int i;
for (i = 0; i < dict_size; i++) {
assert_int_equal(map_set(tree, dict[i], (void *)dict[i]), 0);
}
}
/* Searching */
static void test_get(void **state)
{
map_t *tree = *state;
char *in;
const char *notin = "not in tree";
in = malloc(strlen(dict[23])+1);
strcpy(in, dict[23]);
assert_true(map_get(tree, in) == dict[23]);
assert_true(map_get(tree, notin) == NULL);
assert_true(map_get(tree, "") == NULL);
in[strlen(in)/2] = '\0';
assert_true(map_get(tree, in) == NULL);
free(in);
}
/* Deletion */
static void test_delete(void **state)
{
map_t *tree = *state;
assert_int_equal(map_del(tree, dict[91]), 0);
assert_false(map_contains(tree, dict[91]));
assert_int_equal(map_del(tree, "most likely not in tree"), 1);
}
/* Test null value existence */
static void test_null_value(void **state)
{
map_t *tree = *state;
char *key = "foo";
assert_int_equal(map_set(tree, key, (void *)0), 0);
assert_true(map_contains(tree, key));
assert_int_equal(map_del(tree, key), 0);
}
static void test_init(void **state)
{
static map_t tree;
tree = map_make(NULL);
*state = &tree;
assert_non_null(*state);
}
static void test_deinit(void **state)
{
map_t *tree = *state;
map_clear(tree);
}
/* Program entry point */
int main(int argc, char **argv)
{
const UnitTest tests[] = {
group_test_setup(test_init),
unit_test(test_insert),
unit_test(test_get),
unit_test(test_delete),
unit_test(test_null_value),
group_test_teardown(test_deinit)
};
return run_group_tests(tests);
}
/*
* critbit89 - A crit-bit tree implementation for strings in C89
* Written by Jonas Gehring <jonas@jgehring.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tests/unit/test.h"
#include "lib/generic/set.h"
#include "lib/utils.h"
/*
* Sample dictionary: 100 random words from /usr/share/dict/words
* Generated using random.org:
* MAX=`wc -l < /usr/share/dict/words | tr -d " "`
* for i in `curl "http://www.random.org/integers/?num=100&min=1&max=$MAX&col=1&base=10&format=plain&rnd=new"`; do
* nl /usr/share/dict/words | grep -w $i | tr -d "0-9\t "
* done
*/
static const char *dict[] = {
"catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
"alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
"seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
"physiologically", "economizer", "forcepslike", "betrumpet",
"Danization", "broadthroat", "randir", "usherette", "nephropyosis",
"hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
"siphonognathous", "indoor", "featheriness", "forwardation",
"archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
"unpragmatical", "childless", "transumpt", "productive",
"thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
"pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
"smoothbored", "widower", "sivathere", "pendle", "saltation",
"autopelagic", "campfight", "unexplained", "Macrorhamphosus",
"absconsa", "counterflory", "interdependent", "triact", "reconcentration",
"oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
"pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
"Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
"cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
"bacillogenic", "hamartite", "polytheistical", "unescapableness",
"Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
"perishless", "cupidity", "semilichen", "gadge", "detrimental",
"misencourage", "toparchia", "lurchingly", "apocatastasis"
};
/* Insertions */
static void test_insert(void **state)
{
set_t *set = *state;
int dict_size = sizeof(dict) / sizeof(const char *);
int i;
for (i = 0; i < dict_size; i++) {
assert_int_equal(set_add(set, dict[i]), 0);
}
}
/* Insertion of duplicate element */
static void test_insert_dup(void **state)
{
set_t *set = *state;
int dict_size = sizeof(dict) / sizeof(const char *);
int i;
for (i = 0; i < dict_size; i++) {
if (!set_contains(set, dict[i])) {
continue;
}
assert_int_equal(set_add(set, dict[i]), 1);
}
}
/* Searching */
static void test_contains(void **state)
{
set_t *set = *state;
char *in;
const char *notin = "not in set";
in = malloc(strlen(dict[23])+1);
strcpy(in, dict[23]);
assert_true(set_contains(set, in));
assert_false(set_contains(set, notin));
assert_false(set_contains(set, ""));
in[strlen(in)/2] = '\0';
assert_false(set_contains(set, in));
free(in);
}
/* Count number of items */
static int count_cb(const char *s, void *_, void *n) { (*(int *)n)++; return 0; }
static void test_complete(set_t *set, int n)
{
int i = 0;
if (set_walk(set, count_cb, &i) != 0) {
abort();
}
if (i != n) {
abort();
}
}
static void test_complete_full(void **state) { test_complete(*state, sizeof(dict) / sizeof(const char *)); }
static void test_complete_zero(void **state) { test_complete(*state, 0); }
/* Deletion */
static void test_delete(void **state)
{
set_t *set = *state;
assert_int_equal(set_del(set, dict[91]), 0);
assert_int_equal(set_del(set, "most likely not in set"), 1);
}
/* Complete deletion */
static void test_delete_all(void **state)
{
set_t *set = *state;
int dict_size = sizeof(dict) / sizeof(const char *);
int i;
for (i = 0; i < dict_size; i++) {
if (!set_contains(set, dict[i])) {
continue;
}
assert_int_equal(set_del(set, dict[i]), 0);
}
}
/* Fake allocator */
static void *fake_malloc(void *b, size_t s) { return NULL; }
static void test_allocator(void **state)
{
knot_mm_t fake_pool = { .ctx = NULL, .alloc = fake_malloc, .free = NULL };
set_t set = set_make(&fake_pool);
assert_int_equal(set_add(&set, dict[0]), ENOMEM);
}
/* Empty set */
static void test_empty(void **state)
{
set_t *set = *state;
assert_int_equal(set_contains(set, dict[1]), 0);
assert_int_not_equal(set_del(set, dict[1]), 0);
}
/* Prefix walking */
static void test_prefixes(void **state)
{
set_t *set = *state;
int i = 0;
if ((set_add(set, "1str") != 0) ||
(set_add(set, "11str2") != 0) ||
(set_add(set, "12str") != 0) ||
(set_add(set, "11str") != 0)) {
assert_int_equal(1, 0);
}
assert_int_equal(set_walk_prefixed(set, "11", count_cb, &i), 0);
assert_int_equal(i, 2);
i = 0;
assert_int_equal(set_walk_prefixed(set, "13", count_cb, &i), 0);
assert_int_equal(i, 0);
i = 0;
assert_int_equal(set_walk_prefixed(set, "12345678", count_cb, &i), 0);
assert_int_equal(i, 0);
i = 0;
assert_int_equal(set_walk_prefixed(set, "11str", count_cb, &i), 0);
assert_int_equal(i, 2);
}
static void test_clear(void **state)
{
set_t *set = *state;
set_clear(set);
}
static void test_init(void **state)
{
static set_t set;
set = set_make(NULL);
*state = &set;
assert_non_null(*state);
}
static void test_deinit(void **state)
{
set_t *set = *state;
set_clear(set);
}
/* Program entry point */
int main(int argc, char **argv)
{
const UnitTest tests[] = {
group_test_setup(test_init),
unit_test(test_insert),
unit_test(test_complete_full),
unit_test(test_insert_dup),
unit_test(test_contains),
unit_test(test_delete),
unit_test(test_clear),
unit_test(test_insert),
unit_test(test_complete_full),
unit_test(test_delete_all),
unit_test(test_complete_zero),
unit_test(test_allocator),
unit_test(test_clear),
unit_test(test_empty),
unit_test(test_insert),
unit_test(test_prefixes),
group_test_teardown(test_deinit)
};
return run_group_tests(tests);
}
......@@ -843,6 +843,26 @@ int trie_apply(trie_t *tbl, int (*f)(trie_val_t *, void *), void *d)
return apply_trie(&tbl->root, f, d);
}
/*! \brief Apply a function to every key + trie_val_t*, in order; a recursive solution. */
static int apply_trie_with_key(node_t *t, int (*f)(const char *, uint32_t, trie_val_t *, void *), void *d)
{
kr_require(t);
if (!isbranch(t))
return f(t->leaf.key->chars, t->leaf.key->len, &t->leaf.val, d);
int child_count = bitmap_weight(t->branch.bitmap);
for (int i = 0; i < child_count; ++i)
ERR_RETURN(apply_trie_with_key(twig(t, i), f, d));
return KNOT_EOK;
}
int trie_apply_with_key(trie_t *tbl, int (*f)(const char *, uint32_t, trie_val_t *, void *), void *d)
{
kr_require(tbl && f);
if (!tbl->weight)
return KNOT_EOK;
return apply_trie_with_key(&tbl->root, f, d);
}
/* These are all thin wrappers around static Tns* functions. */
trie_it_t* trie_it_begin(trie_t *tbl)
{
......
......@@ -83,6 +83,17 @@ int trie_get_leq(trie_t *tbl, const char *key, uint32_t len, trie_val_t **val);
KR_EXPORT
int trie_apply(trie_t *tbl, int (*f)(trie_val_t *, void *), void *d);
/*!
* \brief Apply a function to every trie_val_t, in order.
*
* It's like trie_apply() but additionally passes keys and their lengths.
*
* \param d Parameter passed as the second argument to f().
* \return First nonzero from f() or zero (i.e. KNOT_EOK).
*/
KR_EXPORT
int trie_apply_with_key(trie_t *tbl, int (*f)(const char *, uint32_t, trie_val_t *, void *), void *d);
/*!
* \brief Remove an item, returning KNOT_EOK if succeeded or KNOT_ENOENT if not found.
*
......
......@@ -17,7 +17,6 @@ libkres_src = files([
'dnssec/signature.c',
'dnssec/ta.c',
'generic/lru.c',
'generic/map.c',
'generic/queue.c',
'generic/trie.c',
'layer/cache.c',
......@@ -48,7 +47,6 @@ libkres_headers = files([
'dnssec/ta.h',
'generic/array.h',
'generic/lru.h',
'generic/map.h',
'generic/pack.h',
'generic/queue.h',
'generic/trie.h',
......@@ -68,10 +66,8 @@ libkres_headers = files([
unit_tests += [
['array', files('generic/test_array.c')],
['lru', files('generic/test_lru.c')],
['map', files('generic/test_map.c')],
['pack', files('generic/test_pack.c')],
['queue', files('generic/test_queue.c')],
['set', files('generic/test_set.c')],
['trie', files('generic/test_trie.c')],
['module', files('test_module.c')],
['rplan', files('test_rplan.c')],
......
......@@ -11,7 +11,6 @@
#include "lib/cookies/control.h"
#include "lib/cookies/lru_cache.h"
#include "lib/layer.h"
#include "lib/generic/map.h"
#include "lib/generic/array.h"
#include "lib/selection.h"
#include "lib/rplan.h"
......
......@@ -336,10 +336,20 @@ ssize_t kr_sockaddr_key(struct kr_sockaddr_key_storage *dst,
const struct sockaddr_un *addr_un = (const struct sockaddr_un *) addr;
struct kr_sockaddr_un_key *unkey = (struct kr_sockaddr_un_key *) dst;
unkey->family = AF_UNIX;
strncpy(unkey->path, addr_un->sun_path, sizeof(unkey->path));
size_t pathlen = strnlen(unkey->path, sizeof(unkey->path));
if (pathlen < sizeof(unkey->path)) /* Include null-terminator */
pathlen += 1;
size_t pathlen = strnlen(addr_un->sun_path, sizeof(unkey->path));
if (pathlen == 0 || pathlen >= sizeof(unkey->path)) {
/* Abstract sockets are not supported - we would need
* to also supply a length value for the abstract
* pathname.
*
* UNIX socket path should be null-terminated.
*
* See unix(7). */
return kr_error(EINVAL);
}
pathlen += 1; /* Include null-terminator */
strncpy(unkey->path, addr_un->sun_path, pathlen);
return offsetof(struct kr_sockaddr_un_key, path) + pathlen;
default:
......@@ -378,10 +388,48 @@ struct sockaddr *kr_sockaddr_from_key(struct sockaddr_storage *dst,
return (struct sockaddr *) addr_un;
default:
kr_assert(false);
return NULL;
}
}
bool kr_sockaddr_key_same_addr(const char *key_a, const char *key_b)
{
const struct kr_sockaddr_in6_key *kkey_a = (struct kr_sockaddr_in6_key *) key_a;
const struct kr_sockaddr_in6_key *kkey_b = (struct kr_sockaddr_in6_key *) key_b;
if (kkey_a->family != kkey_b->family)
return false;
ptrdiff_t offset;
switch (kkey_a->family) {
case AF_INET:
offset = offsetof(struct kr_sockaddr_in_key, address);
break;
case AF_INET6:
if (unlikely(kkey_a->scope != kkey_b->scope))
return false;
offset = offsetof(struct kr_sockaddr_in6_key, address);
break;
case AF_UNIX:;
const struct kr_sockaddr_un_key *unkey_a =
(struct kr_sockaddr_un_key *) key_a;
const struct kr_sockaddr_un_key *unkey_b =
(struct kr_sockaddr_un_key *) key_b;
return strncmp(unkey_a->path, unkey_b->path,
sizeof(unkey_a->path)) == 0;
default:
kr_assert(false);
return false;
}
size_t len = kr_family_len(kkey_a->family);
return memcmp(key_a + offset, key_b + offset, len) == 0;
}
int kr_sockaddr_cmp(const struct sockaddr *left, const struct sockaddr *right)
{
if (!left || !right) {
......@@ -486,7 +534,10 @@ int kr_straddr_family(const char *addr)
if (strchr(addr, ':')) {
return AF_INET6;
}
return AF_INET;
if (strchr(addr, '.')) {
return AF_INET;
}
return kr_error(EINVAL);
}
int kr_family_len(int family)
......@@ -531,7 +582,6 @@ struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool)
return (struct sockaddr *)res;
}
default:
kr_assert(false);
return NULL;
}
}
......