diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2019-11-25 13:03:37 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2019-11-25 13:03:37 +0000 |
commit | d4350788eea01f04b464d382d74ee0f391a78398 (patch) | |
tree | 908e24ce99ea465b9cd05ea15d61b702c1b883cb /src/caches/dcache_add.c | |
parent | 922841847862b39662b1039304de1361401f214b (diff) | |
download | s6-dns-d4350788eea01f04b464d382d74ee0f391a78398.tar.xz |
Initial libdcache implementation
Diffstat (limited to 'src/caches/dcache_add.c')
-rw-r--r-- | src/caches/dcache_add.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/caches/dcache_add.c b/src/caches/dcache_add.c new file mode 100644 index 0000000..a534f7a --- /dev/null +++ b/src/caches/dcache_add.c @@ -0,0 +1,110 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <skalibs/uint64.h> +#include <skalibs/alloc.h> +#include <skalibs/tai.h> +#include <skalibs/gensetdyn.h> +#include <skalibs/avlnode.h> +#include <skalibs/avltree.h> + +#include <s6-dns/dcache.h> + +#define DNODE(z, i) GENSETDYN_P(dcache_node_t, &(z)->storage, i) +#define DCACHE_NODE_OVERHEAD (32 + sizeof(dcache_node_t) + 3 * sizeof(avlnode)) + +static void uniquify (avltree const *tree, tain_t *stamp) +{ + static tain_t const nano = { .sec = TAI_ZERO, .nano = 1 } ; + uint32_t dummy ; + while (avltree_search(tree, stamp, &dummy)) + tain_add(stamp, stamp, &nano) ; +} + +static void dcache_delete (dcache_t *z, uint32_t i) +{ + dcache_node_t *y = DNODE(z, i) ; + avltree_delete(&z->by_expire, &y->expire) ; + avltree_delete(&z->by_entry, &y->entry) ; + avltree_delete(&z->by_key, &y->key) ; + alloc_free(y->key.s) ; + z->size -= DCACHE_NODE_OVERHEAD + y->key.len + y->datalen ; + gensetdyn_delete(&z->storage, i) ; +} + +static inline void dcache_gc_by_entry (dcache_t *z, uint64_t max) +{ + while (z->size > max) + { + uint32_t oldest ; + if (!avltree_min(&z->by_entry, &oldest)) break ; + dcache_delete(z, oldest) ; + } +} + +static inline void dcache_gc_by_expire (dcache_t *z, tain_t const *stamp) +{ + for (;;) + { + uint32_t i ; + if (!avltree_min(&z->by_expire, &i)) break ; + if (tain_less(stamp, &DNODE(z, i)->expire)) break ; + dcache_delete(z, i) ; + } +} + +static inline int dcache_add_node (dcache_t *z, dcache_node_t const *node) +{ + uint32_t i ; + dcache_node_t *y ; + if (!gensetdyn_new(&z->storage, &i)) return 0 ; + y = DNODE(z, i) ; *y = *node ; + uniquify(&z->by_entry, &y->entry) ; + uniquify(&z->by_expire, &y->expire) ; + if (!avltree_insert(&z->by_key, i)) goto err1 ; + if (!avltree_insert(&z->by_entry, i)) goto err2 ; + if (!avltree_insert(&z->by_expire, i)) goto err3 ; + return 1 ; + + err3: + avltree_delete(&z->by_entry, &y->entry) ; + err2: + avltree_delete(&z->by_key, &y->key) ; + err1: + gensetdyn_delete(&z->storage, i) ; + return 0 ; +} + +static inline int dcache_add_unbounded (dcache_t *z, char const *key, uint16_t keylen, char const *data, uint16_t datalen, tain_t const *expire, tain_t const *stamp) +{ + uint32_t len = keylen + datalen ; + dcache_node_t y = { .key = { .s = alloc(len) } } ; + if (!y.key.s) return 0 ; + memcpy(y.key.s, key, keylen) ; + memcpy(y.key.s + keylen, data, datalen) ; + y.key.len = keylen ; + y.datalen = datalen ; + y.entry = *stamp ; + y.expire = *expire ; + if (!dcache_add_node(z, &y)) + { + alloc_free(y.key.s) ; + return 0 ; + } + z->size += DCACHE_NODE_OVERHEAD + len ; + z->motion += DCACHE_NODE_OVERHEAD + len ; + return 1 ; +} + + +int dcache_add (dcache_t *z, uint64_t max, char const *key, uint16_t keylen, char const *data, uint16_t datalen, tain_t const *expire, tain_t const *stamp) +{ + uint64_t size = DCACHE_NODE_OVERHEAD + keylen + datalen ; + if (size > max) return (errno = EINVAL, 0) ; + if (z->size > max - size) dcache_gc_by_expire(z, stamp) ; + if (z->size > max - size) dcache_gc_by_entry(z, max - size) ; + return dcache_add_unbounded(z, key, keylen, data, datalen, expire, stamp) ; +} |