diff options
author | Hristo Venev <hristo@venev.name> | 2020-06-22 12:32:11 +0300 |
---|---|---|
committer | Hristo Venev <hristo@venev.name> | 2020-06-22 12:54:22 +0300 |
commit | cb2a50691fb0cddb64e1b5a9ed242a6a0b42d503 (patch) | |
tree | 57228e4e9efacb0d2a88d7d8b825a44278075875 /lib |
Diffstat (limited to 'lib')
-rw-r--r-- | lib/db.c | 180 | ||||
-rw-r--r-- | lib/die.c | 21 | ||||
-rw-r--r-- | lib/extfile.c | 150 | ||||
-rw-r--r-- | lib/hash.c | 78 | ||||
-rw-r--r-- | lib/impl.h | 209 | ||||
-rw-r--r-- | lib/internal.h | 106 | ||||
-rw-r--r-- | lib/io_dirfd.c | 276 | ||||
-rw-r--r-- | lib/io_mem.c | 181 | ||||
-rw-r--r-- | lib/iter.c | 43 | ||||
-rw-r--r-- | lib/ix.c | 215 | ||||
-rw-r--r-- | lib/record.c | 352 | ||||
-rw-r--r-- | lib/slab.c | 199 | ||||
-rw-r--r-- | lib/table.c | 212 | ||||
-rw-r--r-- | lib/util.c | 30 |
14 files changed, 2252 insertions, 0 deletions
diff --git a/lib/db.c b/lib/db.c new file mode 100644 index 0000000..81cce47 --- /dev/null +++ b/lib/db.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +#ifdef FUZZING +static const size_t hkvs_slab_sizes[HKVS_N_SLABS] = { + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, + 74, 76, 78, 80, +}; +#else +static const size_t hkvs_slab_sizes[HKVS_N_SLABS] = { + (2 << 6), (3 << 6), (4 << 6), (5 << 6), (6 << 6), (7 << 6), + (8 << 6), (9 << 6), (10 << 6), (11 << 6), (12 << 6), (14 << 6), + (8 << 7), (9 << 7), (10 << 7), (11 << 7), (12 << 7), (14 << 7), + (8 << 8), (9 << 8), (10 << 8), (11 << 8), (12 << 8), (14 << 8), + (8 << 9), (9 << 9), (10 << 9), (11 << 9), (12 << 9), (14 << 9), + (8 << 10), (9 << 10), (10 << 10), (11 << 10), (12 << 10), (14 << 10), + (8 << 11), (9 << 11), (10 << 11), (11 << 11), +}; +#endif + +int hkvs_new(hkvs **pc, hkvs_io *io) { + int r; + bool created = false; + + hkvs *c = malloc(sizeof(*c)); + if(!c) return -ENOMEM; + + c->io = io; + + r = io->open(io, 0, &c->root_param, &c->root_param_size); + if(r < 0) { + goto fail_free; + } + + char *at = c->root_param; + size_t param_size = c->root_param_size; + if(param_size == 0) { + r = io->resize(io, 0, &c->root_param, param_size, HKVS_POFF_TABLE); + if(r < 0) goto fail_close; + at = c->root_param; + param_size = HKVS_POFF_TABLE; + c->root_param_size = param_size; + memset(at, 0, HKVS_POFF_TABLE); + hkvs_write_u64(at + HKVS_PARAM_HEADER_MAGIC, HKVS_MAGIC); + created = true; + } else if(param_size < HKVS_PSIZE_HEADER || hkvs_read_u64(at + HKVS_PARAM_HEADER_MAGIC) != HKVS_MAGIC) { + r = -EBADMSG; + goto fail_close; + } + + uint64_t version = hkvs_read_u64(at + HKVS_PARAM_HEADER_VERSION); + if(version != 0) { + r = -ENOTSUP; + goto fail_close; + } + + uint64_t n_tables_u64 = hkvs_read_u64(at + 16); + at += HKVS_PSIZE_HEADER; + + if(param_size < HKVS_POFF_TABLE || n_tables_u64 > (param_size - HKVS_POFF_TABLE) / HKVS_PSIZE_TABLE) { + r = -EBADMSG; + goto fail_close; + } + size_t n_tables = (size_t)n_tables_u64; + + for(size_t i = 0; i < HKVS_N_SLABS; i++) { + r = hkvs_slab_init(&c->slabs[i], io, at, hkvs_slab_sizes[i]); + at += HKVS_PSIZE_SLAB; + if(r < 0) goto fail_slabs; + } + + hkvs_table **tables = calloc(n_tables, sizeof(hkvs_table*)); + c->tables = tables; + c->n_tables = n_tables; + c->alloc_tables = n_tables; + for(size_t i = 0; i < n_tables; i++) { + r = hkvs_table_new(&tables[i], io, at); + at += HKVS_PSIZE_TABLE; + if(r < 0) goto fail_tables; + } + + c->extfiles = NULL; + c->n_extfiles = 0; + c->alloc_extfiles = 0; + c->extfile_gen = 0; + + *pc = c; + return created; + +fail_tables: + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(t) hkvs_table_free(t, io); + } + free(tables); + +fail_slabs:; + size_t n_slabs = (size_t)(at - (char*)c->root_param) / HKVS_PSIZE_SLAB; + if(n_slabs > HKVS_N_SLABS) n_slabs = HKVS_N_SLABS; + while(n_slabs--) { + hkvs_slab_destroy(&c->slabs[n_slabs], io); + } + +fail_close: + io->close(io, 0, c->root_param, c->root_param_size); +fail_free: + free(c); + io->destroy(io); + return r; +} + +int hkvs_resize_param(hkvs *c) { + if(c->n_tables > (SIZE_MAX - HKVS_POFF_TABLE) / HKVS_PSIZE_TABLE) return -EFBIG; + + size_t param_size = HKVS_POFF_TABLE + c->n_tables * HKVS_PSIZE_TABLE; + int r = c->io->resize(c->io, 0, &c->root_param, c->root_param_size, param_size); + if(r < 0) return r; + c->root_param_size = param_size; + + char *at = c->root_param; + if(hkvs_read_u64(at + HKVS_PARAM_HEADER_MAGIC) != HKVS_MAGIC || hkvs_read_u64(at + HKVS_PARAM_HEADER_VERSION) != 0) { + hkvs_die("magic corrupted"); + } + hkvs_write_u64(at + 16, c->n_tables); + at += HKVS_PSIZE_HEADER; + + for(size_t i = 0; i < HKVS_N_SLABS; i++) { + c->slabs[i].param = at; + at += HKVS_PSIZE_SLAB; + } + + hkvs_table **tables = c->tables; + size_t n_tables = c->n_tables; + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(t) t->slab.param = at; + at += HKVS_PSIZE_TABLE; + } + + return 0; +} + +void hkvs_free(hkvs *c) { + hkvs_io *io = c->io; + + hkvs_extfile *ext = c->extfiles; + size_t n_ext = c->n_extfiles; + for(size_t i = 0; i < n_ext; i++) { + io->close(io, ext[i].fi, ext[i].addr, ext[i].size); + } + free(ext); + + hkvs_table **tables = c->tables; + size_t n_tables = c->n_tables; + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(t) hkvs_table_free(t, io); + } + free(tables); + + for(size_t i = 0; i < HKVS_N_SLABS; i++) { + hkvs_slab_destroy(&c->slabs[i], io); + } + + io->close(io, 0, c->root_param, c->root_param_size); + free(c); + io->destroy(io); + return; +} + +uint8_t hkvs_slab_for_size(size_t data_size) { + // TODO: faster + for(uint8_t i = 0; i < HKVS_N_SLABS; i++) { + if(data_size <= hkvs_slab_sizes[i]) return i; + } + return 255; +} diff --git a/lib/die.c b/lib/die.c new file mode 100644 index 0000000..f581951 --- /dev/null +++ b/lib/die.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#undef hkvs_die +void hkvs_die(const char *msg) { + hkvs_die_ext(NULL, NULL, 0, msg); +} + +void hkvs_die_ext(const char *func, const char *file, int line, const char *msg) { + (void)file; + if(func) { + fprintf(stderr, "Internal error: [%s:%d] %s\n", func, line, msg); + } else { + fprintf(stderr, "Internal error: %s\n", msg); + } + abort(); +} diff --git a/lib/extfile.c b/lib/extfile.c new file mode 100644 index 0000000..fdd94eb --- /dev/null +++ b/lib/extfile.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +#define MAX_GEN ((uint32_t)2147483648u) + +static bool hkvs_extfile_gc(hkvs *c) { + hkvs_extfile *ext = c->extfiles; + size_t n = c->n_extfiles; + + uint32_t gen = c->extfile_gen; + uint32_t max = 0; + for(size_t i = 0; i < n; i++) { + uint32_t diff = gen - ext[i].gen; + if(diff > max) max = diff; + } + if(max == 0) return false; + + if(max >= MAX_GEN) max = MAX_GEN; + + hkvs_io *io = c->io; + size_t i = 0; + for(size_t j = 0; j < n; j++) { + uint32_t diff = gen - ext[j].gen; + if(diff >= max) { + io->close(io, ext[j].fi, ext[j].addr, ext[j].size); + } else { + ext[i++] = ext[j]; + } + } + + if(n > 64 && i < n / 2) { + ext = realloc(ext, i * sizeof(hkvs_extfile)); + if(ext) { + c->extfiles = ext; + c->alloc_extfiles = i; + } + } + + c->n_extfiles = i; + return true; +} + +static int hkvs_extfile_reserve_slow(hkvs *c) { + if(c->n_extfiles > 16) { + if(hkvs_extfile_gc(c)) return 0; + } + + size_t n = c->n_extfiles; + hkvs_extfile *ext = c->extfiles; + n += (n >> 2) + 1; + ext = realloc(ext, n * sizeof(hkvs_extfile)); + if(!ext) return -ENOMEM; + c->extfiles = ext; + c->alloc_extfiles = n; + return 0; +} + +HKVS_INLINE int hkvs_extfile_reserve(hkvs *c) { + if(c->n_extfiles != c->alloc_extfiles) return 0; + return hkvs_extfile_reserve_slow(c); +} + +int hkvs_extfile_create(hkvs *c, uint64_t *pfi, char **paddr, size_t size, bool pin) { + int r = hkvs_extfile_reserve(c); + if(r < 0) return r; + + uint64_t fi; + char *addr; + r = c->io->create(c->io, &fi, &addr, size); + if(r < 0) return r; + if(fi == 0) hkvs_die("created file 0"); + + c->extfiles[c->n_extfiles++] = (hkvs_extfile) { + .fi = fi, + .addr = addr, + .size = size, + .gen = pin ? c->extfile_gen : c->extfile_gen - 1, + }; + + *pfi = fi; + *paddr = addr; + + return 0; +} + +HKVS_MUSTCHECK bool hkvs_extfile_find(hkvs *c, uint64_t fi, size_t *pslot) { + hkvs_extfile *ext = c->extfiles; + size_t n = c->n_extfiles; + for(size_t i = 0; i < n; i++) { + if(ext[i].fi == fi) { + *pslot = i; + return true; + } + } + return false; +} + +HKVS_MUSTCHECK int hkvs_extfile_open(hkvs *c, uint64_t fi, size_t *pslot, size_t want_size) { + if(fi == 0) return -EBADMSG; + + hkvs_extfile *ext = c->extfiles; + size_t n = c->n_extfiles; + for(size_t i = 0; i < n; i++) { + if(ext[i].fi == fi) { + if(ext[i].size < want_size) return -EBADMSG; + *pslot = i; + return 0; + } + } + + int r = hkvs_extfile_reserve(c); + if(r < 0) return 0; + + char *addr; + size_t size; + r = c->io->open(c->io, fi, &addr, &size); + if(r < 0) return r; + + size_t slot = c->n_extfiles++; + c->extfiles[slot] = (hkvs_extfile) { + .fi = fi, + .addr = addr, + .size = size, + .gen = c->extfile_gen - 1, + }; + + if(size < want_size) return -EBADMSG; + + *pslot = slot; + return 0; +} + +void hkvs_extfile_close(hkvs *c, size_t slot) { + hkvs_extfile *ext = c->extfiles; + size_t n = c->n_extfiles; + hkvs_assume(slot < n); + n--; + c->n_extfiles = n; + + c->io->close(c->io, ext[slot].fi, ext[slot].addr, ext[slot].size); + if(slot != n) ext[slot] = ext[n]; +} + +void hkvs_put_records(hkvs *c) { + c->extfile_gen++; + if(c->extfile_gen % MAX_GEN == 0) { + hkvs_extfile_gc(c); + } +} diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 0000000..6a969a8 --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +#define rotl(v, x) (((v) << (x)) | ((v) >> (64 - (x)))) + +#define ROUND do { \ + v0 += v1; \ + v2 += v3; \ + v1 = rotl(v1, 13); \ + v3 = rotl(v3, 16); \ + v1 ^= v0; \ + v3 ^= v2; \ + v0 = rotl(v0, 32); \ + v2 += v1; \ + v0 += v3; \ + v1 = rotl(v1, 17); \ + v3 = rotl(v3, 21); \ + v1 ^= v2; \ + v3 ^= v0; \ + v2 = rotl(v2, 32); \ +} while(0) + +uint64_t hkvs_hash(const char *hk, const char *data, size_t data_size) { + uint64_t hk0 = hkvs_read_u64(hk + 0); + uint64_t hk1 = hkvs_read_u64(hk + 8); + uint64_t v0 = (uint64_t)0x736f6d6570736575 ^ hk0; + uint64_t v1 = (uint64_t)0x646f72616e646f6d ^ hk1; + uint64_t v2 = (uint64_t)0x6c7967656e657261 ^ hk0; + uint64_t v3 = (uint64_t)0x7465646279746573 ^ hk1; + uint64_t last = (uint64_t)data_size << 56; + while(data_size >= 8) { + uint64_t m = hkvs_read_u64(data); + v3 ^= m; + ROUND; + v0 ^= m; + data += 8; + data_size -= 8; + } + + switch(data_size) { + case 7: + last |= (uint64_t)(uint8_t)data[6] << 48; + HKVS_FALLTHROUGH; + case 6: + last |= (uint64_t)(uint8_t)data[5] << 40; + HKVS_FALLTHROUGH; + case 5: + last |= (uint64_t)(uint8_t)data[4] << 32; + HKVS_FALLTHROUGH; + case 4: + last |= (uint64_t)(uint8_t)data[3] << 24; + HKVS_FALLTHROUGH; + case 3: + last |= (uint64_t)(uint8_t)data[2] << 16; + HKVS_FALLTHROUGH; + case 2: + last |= (uint64_t)(uint8_t)data[1] << 8; + HKVS_FALLTHROUGH; + case 1: + last |= (uint64_t)(uint8_t)data[0]; + HKVS_FALLTHROUGH; + case 0: + break; + default: + __builtin_unreachable(); + } + + v3 ^= last; + ROUND; + v0 ^= last; + + v2 ^= 0xff; + ROUND; + ROUND; + ROUND; + return v0 ^ v1 ^ v2 ^ v3; +} diff --git a/lib/impl.h b/lib/impl.h new file mode 100644 index 0000000..47a05ca --- /dev/null +++ b/lib/impl.h @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include "internal.h" + +#ifdef FUZZING +#define HKVS_SLAB_BLOCK ((size_t)16) +#else +#define HKVS_SLAB_BLOCK ((size_t)4096) +#endif + +#define HKVS_REWRITE_THRESHOLD ((size_t)8192) + +#define HKVS_SLAB_FREEPTRS ((size_t)8) + +#define HKVS_N_SLABS ((uint8_t)40) + +#define HKVS_MARK_MAX_SMALL ((uint8_t)249) +#define HKVS_MARK_EXT ((uint8_t)255) +#define HKVS_MARK_SLAB ((uint8_t)254) + +#define HKVS_IX_ENTRY ((size_t)16) +#define HKVS_IX_KEY_SIZE ((size_t)16) +#define HKVS_IX_MAX_DISP ((size_t)6) +#define HKVS_IX_SIZE(bits) ((uint64_t)HKVS_IX_ENTRY << (bits)) + +#define HKVS_PSIZE_HEADER ((size_t)64) +#define HKVS_PARAM_HEADER_MAGIC ((size_t)0) +#define HKVS_PARAM_HEADER_VERSION ((size_t)8) +/* + * header { + * magic: u64, + * version: u64, + * app_id: u64, + * app_ver: u64, + * pad: [u64; 4], + * } + */ + +#define HKVS_POFF_SLAB HKVS_PSIZE_HEADER +#define HKVS_PSIZE_SLAB ((size_t)32) +#define HKVS_PARAM_SLAB_FI ((size_t)0) +#define HKVS_PARAM_SLAB_ALLOC ((size_t)8) +#define HKVS_PARAM_SLAB_FREE ((size_t)16) +/* + * slab { + * fi: u64, + * alloc_count: u64, + * free: u64, + * pad: u64, + * } + */ + +#define HKVS_POFF_TABLE ((size_t)(HKVS_POFF_SLAB + HKVS_N_SLABS * HKVS_PSIZE_SLAB)) +#define HKVS_PSIZE_TABLE ((size_t)64) +#define HKVS_PARAM_TABLE_ELTSZ ((size_t)32) +#define HKVS_PARAM_TABLE_KEYSZ ((size_t)34) +#define HKVS_PARAM_TABLE_IXBIT ((size_t)35) +#define HKVS_PARAM_TABLE_ID ((size_t)36) +#define HKVS_PARAM_TABLE_IXFI ((size_t)40) +#define HKVS_PARAM_TABLE_IXKEY ((size_t)48) +/* table { + * slab; + * elt_size: u16, + * key_size: u8, + * ix_bits: u8, + * id: u32, + * ix_fi: u64, + * ix_key: [u64; 2], + * } + */ + +typedef struct hkvs_slab hkvs_slab; +struct hkvs_slab { + char *param; + char *addr; + size_t map_size; + size_t elt_size; +}; + +typedef struct hkvs_table hkvs_table; +struct hkvs_table { + hkvs_slab slab; + char *ix; + size_t ix_size; +}; + +typedef struct hkvs_extfile hkvs_extfile; +struct hkvs_extfile { + uint64_t fi; + char *addr; + size_t size; + uint32_t gen; +}; + +struct hkvs { + hkvs_io *io; + + char *root_param; + size_t root_param_size; + + hkvs_table **tables; + size_t n_tables; + size_t alloc_tables; + + hkvs_extfile *extfiles; + size_t n_extfiles; + size_t alloc_extfiles; + uint32_t extfile_gen; + + hkvs_slab slabs[HKVS_N_SLABS]; +}; + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_extfile_create(hkvs *c, uint64_t *pfi, char **paddr, size_t size, bool pin); + +HKVS_INTERNAL HKVS_MUSTCHECK bool hkvs_extfile_find(hkvs *c, uint64_t fi, size_t *pslot); + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_extfile_open(hkvs *c, uint64_t fi, size_t *pslot, size_t want_size); + +HKVS_INTERNAL void hkvs_extfile_close(hkvs *c, size_t slot); + +HKVS_INLINE void hkvs_extfile_pin(hkvs *c, hkvs_extfile *f) { + f->gen = c->extfile_gen; +} + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_resize_param(hkvs *c); + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_slab_init(hkvs_slab *s, hkvs_io *io, char *param, size_t elt_size); + +HKVS_INLINE void hkvs_slab_destroy(hkvs_slab *s, hkvs_io *io) { + uint64_t fi = hkvs_read_u64(s->param); + if(fi) { + io->close(io, fi, s->addr, s->map_size); + } +} + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_slab_alloc(hkvs *c, hkvs_slab *s, size_t *pi); + +HKVS_INTERNAL void hkvs_slab_discard(hkvs_slab *s, size_t id); + +HKVS_INLINE HKVS_PURE size_t hkvs_slab_size(hkvs_slab *s) { + uint64_t r = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_ALLOC); + hkvs_assume(r <= SIZE_MAX / 2); + return (size_t)r; +} + +HKVS_INLINE char *hkvs_slab_mark_at(hkvs_slab *s, size_t i) { + hkvs_assume(i <= SIZE_MAX / 2); + hkvs_assume(i < hkvs_slab_size(s)); + size_t j = i & ~(HKVS_SLAB_BLOCK - 1); + return s->addr + j * s->elt_size + i; +} + +HKVS_INLINE char *hkvs_slab_at(hkvs_slab *s, size_t i) { + hkvs_assume(i <= SIZE_MAX / 2); + hkvs_assume(i < hkvs_slab_size(s)); + size_t j = i & ~(HKVS_SLAB_BLOCK - 1); + return s->addr + i * s->elt_size + j + HKVS_SLAB_BLOCK; +} + +HKVS_INLINE uint8_t hkvs_slab_mark(hkvs_slab *s, size_t i) { + return hkvs_read_u8(hkvs_slab_mark_at(s, i)); +} + +HKVS_INLINE void hkvs_slab_set_mark(hkvs_slab *s, size_t i, uint8_t v) { + hkvs_write_u8(hkvs_slab_mark_at(s, i), v); +} + +HKVS_INTERNAL HKVS_CONST uint8_t hkvs_slab_for_size(size_t data_size); + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_table_new(hkvs_table **t, hkvs_io *io, char *param); + +HKVS_INLINE HKVS_PURE uint32_t hkvs_table__id(hkvs_table *t) { + return hkvs_read_u32(t->slab.param + HKVS_PARAM_TABLE_ID); +} + +HKVS_INLINE HKVS_PURE uint8_t hkvs_table__key_size(hkvs_table *t) { + return hkvs_read_u8(t->slab.param + HKVS_PARAM_TABLE_KEYSZ); +} + +HKVS_INLINE uint8_t hkvs_table__inl_size(hkvs_table *t) { + size_t r = t->slab.elt_size; + size_t key_size = hkvs_table__key_size(t); + hkvs_assume(key_size <= r); + r -= key_size; + if(r > 248) r = 248; + return (uint8_t)r; +} + +HKVS_INLINE HKVS_PURE uint8_t hkvs_table__ix_bits(hkvs_table *t) { + return hkvs_read_u8(t->slab.param + HKVS_PARAM_TABLE_IXBIT); +} + +HKVS_INLINE HKVS_PURE const char *hkvs_table__ix_key(hkvs_table *t) { + return t->slab.param + HKVS_PARAM_TABLE_IXKEY; +} + +HKVS_INTERNAL int hkvs_record__value(hkvs *c, hkvs_table *t, size_t id, char *key, size_t key_size, int notfound, char **pvalue, size_t *psize); + +HKVS_INLINE void hkvs_table_free(hkvs_table *t, hkvs_io *io) { + hkvs_slab_destroy(&t->slab, io); + uint64_t ix_fi = hkvs_read_u64(t->slab.param + HKVS_PARAM_TABLE_IXFI); + io->close(io, ix_fi, t->ix, t->ix_size); + free(t); +} + +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_ix_build(hkvs_table *t, hkvs_io *io); +HKVS_INTERNAL HKVS_MUSTCHECK int hkvs_ix_add(hkvs *c, hkvs_table *t, size_t id); +HKVS_INTERNAL void hkvs_ix_remove(hkvs_table *t, size_t id); diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..3ad3ac5 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#define HKVS_API extern __attribute__((__visibility__("protected"), __nothrow__)) +#define HKVS_INTERNAL extern __attribute__((__visibility__("hidden"), __nothrow__)) + +#define HKVS_PURE __attribute__((__pure__)) + +#define HKVS_CONST __attribute__((__const__)) + +#define HKVS_NORETURN __attribute__((__noreturn__, __cold__)) + +#define HKVS_FALLTHROUGH __attribute__((__fallthrough__)) + +#include <hkvs.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +HKVS_INLINE void hkvs_assume(bool cond) { + if(!cond) { +#ifdef HKVS_FUZZING + hkvs_die("assumption failed"); +#else + __builtin_unreachable(); +#endif + } +} + +HKVS_INLINE uint8_t hkvs_read_u8(const char *pv) { + const uint8_t *p = (const uint8_t*)pv; + return *p; +} + +HKVS_INLINE void hkvs_write_u8(char *pv, uint8_t v) { + uint8_t *p = (uint8_t*)pv; + p[0] = v; +} +HKVS_INLINE uint16_t hkvs_read_u16(const char *pv) { + const uint8_t *p = (const uint8_t*)pv; + return (uint16_t)( + (uint16_t)p[0] | + (uint16_t)p[1] << 8 + ); +} + +HKVS_INLINE void hkvs_write_u16(char *pv, uint16_t v) { + uint8_t *p = (uint8_t*)pv; + p[0] = (uint8_t)(v >> 0); + p[1] = (uint8_t)(v >> 8); +} + +HKVS_INLINE uint32_t hkvs_read_u32(const char *pv) { + const uint8_t *p = (const uint8_t*)pv; + return (uint32_t)( + (uint32_t)p[0] | + (uint32_t)p[1] << 8 | + (uint32_t)p[2] << 16 | + (uint32_t)p[3] << 24 + ); +} + +HKVS_INLINE void hkvs_write_u32(char *pv, uint32_t v) { + uint8_t *p = (uint8_t*)pv; + p[0] = (uint8_t)(v >> 0); + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)(v >> 16); + p[3] = (uint8_t)(v >> 24); +} + +HKVS_INLINE uint64_t hkvs_read_u64(const char *pv) { + const uint8_t *p = (const uint8_t*)pv; + return (uint64_t)( + (uint64_t)p[0] | + (uint64_t)p[1] << 8 | + (uint64_t)p[2] << 16 | + (uint64_t)p[3] << 24 | + (uint64_t)p[4] << 32 | + (uint64_t)p[5] << 40 | + (uint64_t)p[6] << 48 | + (uint64_t)p[7] << 56 + ); +} + +HKVS_INLINE void hkvs_write_u64(char *pv, uint64_t v) { + uint8_t *p = (uint8_t*)pv; + p[0] = (uint8_t)(v >> 0); + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)(v >> 16); + p[3] = (uint8_t)(v >> 24); + p[4] = (uint8_t)(v >> 32); + p[5] = (uint8_t)(v >> 40); + p[6] = (uint8_t)(v >> 48); + p[7] = (uint8_t)(v >> 56); +} + +HKVS_INTERNAL HKVS_NORETURN void hkvs_die(const char *msg); + +HKVS_INTERNAL HKVS_NORETURN void hkvs_die_ext(const char *func, const char *file, int line, const char *msg); + +#define hkvs_die(msg) hkvs_die_ext(__func__, __FILE__, __LINE__, msg) + +HKVS_INTERNAL HKVS_PURE uint64_t hkvs_hash(const char *hk, const char *data, size_t data_size); diff --git a/lib/io_dirfd.c b/lib/io_dirfd.c new file mode 100644 index 0000000..252658c --- /dev/null +++ b/lib/io_dirfd.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#define _GNU_SOURCE +#include <hkvs.h> + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/mman.h> +#include <sys/random.h> +#include <sys/stat.h> +#include <unistd.h> + +typedef struct hkvs_dirfd hkvs_dirfd; +struct hkvs_dirfd { + hkvs_io base; + int fd; + uint64_t create_fi; +}; + +static const char *const base32 = "ABCDEFGHIJKLMNOPQRSTUVWXTZ234567"; + +HKVS_INLINE bool hkvs_dirfd_check_size(size_t size) { + return (size_t)(off64_t)size == size && (off64_t)size >= 0; +} + +static void hkvs_dirfd_name(char *name, uint64_t fi) { + name[0] = 'd'; + name[1] = 'a'; + name[2] = 't'; + name[3] = 'a'; + name[4] = '.'; + name[18] = 0; + { + uint64_t val = fi; + for(size_t i = 0; i < 13; i++) { + name[17 - i] = base32[val % 32]; + val /= 32; + } + } +} + +static void hkvs_dirfd_destroy(hkvs_io *iov) { + hkvs_dirfd *io = (hkvs_dirfd*)iov; + (void) close(io->fd); + free(io); +} + +static int hkvs_dirfd_random(hkvs_io *iov, char *buf, size_t n) { + (void)iov; + while(n) { + ssize_t r = getrandom(buf, n, 0); + if(r < 0) return -errno; + size_t k = (size_t)r; + buf += k; + n -= k; + } + return 0; +} + +static int hkvs_dirfd_open(hkvs_io *iov, uint64_t fi, char **pmem, size_t *psize) { + hkvs_dirfd *io = (hkvs_dirfd*)iov; + + int r; + if(fi == 0) { + r = openat(io->fd, "main", O_RDWR | O_CREAT | O_CLOEXEC, 0666); + } else { + char name[32]; + hkvs_dirfd_name(name, fi); + r = openat(io->fd, name, O_RDWR | O_CLOEXEC, 0666); + } + if(r < 0) return -errno; + int fd = r; + + off64_t fsize = lseek64(fd, 0, SEEK_END); + size_t size = (size_t)fsize; + if((off64_t)size != fsize) { + r = -EFBIG; + goto end; + } + + void *addr; + if(size == 0) { + addr = NULL; + } else { + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if(addr == MAP_FAILED) { + r = -errno; + goto end; + } + } + + *psize = size; + *pmem = addr; + r = 0; + +end: + close(fd); + return r; +} + +static int hkvs_dirfd_create(hkvs_io *iov, uint64_t *pfi, char **pmem, size_t size) { + hkvs_dirfd *io = (hkvs_dirfd*)iov; + uint64_t fi = io->create_fi; + if(fi == 0) abort(); + + if(!hkvs_dirfd_check_size(size)) return -EFBIG; + + int fd; + char name[32]; + hkvs_dirfd_name(name, fi); + while(true) { + int r = openat(io->fd, name, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0666); + if(r >= 0) { + fd = r; + break; + } + r = errno; + if(r != EEXIST) return -r; + fi++; + uint64_t val = fi; + char *p = name + 17; + while(true) { + *p = base32[val % 32]; + if(val % 32 != 0) break; + val /= 32; + p--; + } + } + + io->create_fi = fi + 1; + + int r = ftruncate64(fd, (off64_t)size); + if(r < 0) { + r = -errno; + goto fail; + return r; + } + + void *addr; + if(size == 0) { + addr = NULL; + } else { + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if(addr == MAP_FAILED) { + r = -errno; + goto fail; + } + } + + *pfi = fi; + *pmem = addr; + r = 0; + +end: + close(fd); + return r; + +fail: + (void) unlinkat(io->fd, name, 0); + goto end; +} + +static void hkvs_dirfd_close(hkvs_io *iov, uint64_t fi, char *pmem, size_t size) { + (void)iov; + (void)fi; + + if(size == 0) return; + + int r = munmap(pmem, size); + if(r < 0) { + r = errno; + // TODO + (void) r; + abort(); + } +} + +static int hkvs_dirfd_resize(hkvs_io *iov, uint64_t fi, char **pmem, size_t old_size, size_t new_size) { + hkvs_dirfd *io = (hkvs_dirfd*)iov; + + if(!hkvs_dirfd_check_size(new_size)) return -EFBIG; + + int r; + if(fi == 0) { + r = openat(io->fd, "main", O_RDWR | O_CREAT | O_CLOEXEC, 0666); + } else { + char name[32]; + hkvs_dirfd_name(name, fi); + r = openat(io->fd, name, O_RDWR | O_CLOEXEC, 0666); + } + if(r < 0) return -errno; + int fd = r; + + void *addr = *pmem; + if(new_size > old_size) { + r = ftruncate64(fd, (off64_t)new_size); + if(r < 0) goto fail; + + if(addr) { + addr = mremap(addr, old_size, new_size, MREMAP_MAYMOVE); + } else { + addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + } + if(addr == MAP_FAILED) { + r = -errno; + (void) ftruncate64(fd, (off64_t)old_size); + goto end; + } + *pmem = addr; + } else if(new_size > old_size) { + if(new_size == 0) { + munmap(addr, old_size); + addr = NULL; + } else { + addr = mremap(addr, old_size, new_size, MREMAP_MAYMOVE); + if(addr == MAP_FAILED) goto fail; + } + + r = ftruncate64(fd, (off64_t)old_size); + if(r < 0) { + // TODO + (void) r; + } + + *pmem = addr; + } + + r = 0; + +end: + close(fd); + return r; + +fail: + r = -errno; + goto end; +} + +static void hkvs_dirfd_unlink(hkvs_io *iov, uint64_t fi) { + hkvs_dirfd *io = (hkvs_dirfd*)iov; + + if(fi == 0) abort(); + + char name[32]; + hkvs_dirfd_name(name, fi); + int r = unlinkat(io->fd, name, 0); + if(r < 0) { + r = errno; + // TODO + (void) r; + } else { + if(fi < io->create_fi) { + io->create_fi = fi; + } + } +} + +hkvs_io *hkvs_io_new_unsafe_dirfd(int fd) { + hkvs_dirfd *io = malloc(sizeof(*io)); + if(!io) return NULL; + io->base = (hkvs_io){ + .destroy = &hkvs_dirfd_destroy, + .random = &hkvs_dirfd_random, + .open = &hkvs_dirfd_open, + .create = &hkvs_dirfd_create, + .resize = &hkvs_dirfd_resize, + .close = &hkvs_dirfd_close, + .unlink = &hkvs_dirfd_unlink, + }; + io->fd = fd; + io->create_fi = 1; + return &io->base; +} diff --git a/lib/io_mem.c b/lib/io_mem.c new file mode 100644 index 0000000..4f27c02 --- /dev/null +++ b/lib/io_mem.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "internal.h" +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +typedef struct hkvs_iomem_file hkvs_iomem_file; +struct hkvs_iomem_file { + uintptr_t data_mode; + size_t size; +}; + +typedef struct hkvs_iomem hkvs_iomem; +struct hkvs_iomem { + hkvs_io base; + uint64_t rand_ctr; + char rand_key[16]; + size_t n_files; + struct hkvs_iomem_file *files; +}; + +static void hkvs_iomem_destroy(hkvs_io *iov) { + hkvs_iomem *io = (hkvs_iomem*)iov; + size_t n = io->n_files; + for(size_t i = 0; i < n; i++) { + struct hkvs_iomem_file *f = io->files + i; + uintptr_t mode = f->data_mode & 3; + char *p = (char*)(f->data_mode - mode); + free(p); + } + free(io->files); + free(io); +} + +static int hkvs_iomem_random(hkvs_io *iov, char *buf, size_t n) { + hkvs_iomem *io = (hkvs_iomem*)iov; + while(true) { + uint64_t h = hkvs_hash(io->rand_key, (const char*)&io->rand_ctr, sizeof(io->rand_ctr)); + io->rand_ctr++; + if(n > 8) { + memcpy(buf, &h, 8); + buf += 8; + n -= 8; + } else { + memcpy(buf, &h, n); + return 0; + } + } +} + +static int hkvs_iomem_open(hkvs_io *iov, uint64_t fi, char **pmem, size_t *psz) { + hkvs_iomem *io = (hkvs_iomem*)iov; + + if(fi >= io->n_files) return -ENOENT; + + hkvs_iomem_file *f = io->files + (size_t)fi; + uintptr_t mode = f->data_mode & 3; + char *p = (char*)(f->data_mode - mode); + if(mode == 0) { + if(fi != 0) return -ENOENT; + } else if(mode != 1) { + hkvs_die("bad file state"); + } + + f->data_mode = (uintptr_t)p + 2; + *pmem = p; + *psz = f->size; + return 0; +} + +static int hkvs_iomem_create(hkvs_io *iov, uint64_t *pfi, char **pmem, size_t sz) { + hkvs_iomem *io = (hkvs_iomem*)iov; + + hkvs_iomem_file *f = io->files; + size_t n = io->n_files; + for(size_t i = 1; i < n; i++) { + if(!f[i].data_mode) { + n = i; + f += n; + goto done; + } + } + + f = realloc(f, (n + 1) * sizeof(hkvs_iomem_file)); + if(!f) return -ENOMEM; + io->files = f; + io->n_files = n + 1; + f += n; + f->data_mode = 0; + +done:; + char *p = malloc(sz); + if(!p) return -ENOMEM; + memset(p, 0, sz); + + f->data_mode = (uintptr_t)p + 2; + f->size = sz; + *pfi = n; + *pmem = p; + return 0; +} + +static int hkvs_iomem_resize(hkvs_io *iov, uint64_t fi, char **pmem, size_t old_sz, size_t new_sz) { + hkvs_iomem *io = (hkvs_iomem*)iov; + + if(fi > io->n_files) hkvs_die("bad file index"); + + hkvs_iomem_file *f = io->files + (size_t)fi; + uintptr_t mode = f->data_mode & 3; + char *p = (char*)(f->data_mode - mode); + if(mode != 2) hkvs_die("bad file state"); + if(*pmem != p) hkvs_die("bad file mem"); + if(old_sz != f->size) hkvs_die("bad file old_size"); + + p = realloc(p, new_sz); + if(new_sz && !p) return -ENOMEM; + + f->data_mode = (uintptr_t)p + 2; + f->size = new_sz; + *pmem = p; + return 0; +} + +static void hkvs_iomem_close(hkvs_io *iov, uint64_t fi, char *mem, size_t sz) { + hkvs_iomem *io = (hkvs_iomem*)iov; + + if(fi > io->n_files) hkvs_die("bad file index"); + + hkvs_iomem_file *f = io->files + (size_t)fi; + uintptr_t mode = f->data_mode & 3; + char *p = (char*)(f->data_mode - mode); + if(mode != 2) hkvs_die("bad file state"); + if(mem != p) hkvs_die("bad file mem"); + if(sz != f->size) hkvs_die("bad file size"); + + f->data_mode = (uintptr_t)p + 1; +} + +static void hkvs_iomem_unlink(hkvs_io *iov, uint64_t fi) { + hkvs_iomem *io = (hkvs_iomem*)iov; + + if(fi > io->n_files) hkvs_die("bad file index"); + + hkvs_iomem_file *f = io->files + (size_t)fi; + uintptr_t mode = f->data_mode & 3; + char *p = (char*)(f->data_mode - mode); + if(mode != 1) hkvs_die("bad file state"); + + free(p); + f->data_mode = 0; + f->size = 0; +} + +hkvs_io *hkvs_io_new_mem(uint64_t rand_seed) { + hkvs_iomem_file *files = malloc(sizeof(hkvs_iomem_file)); + if(!files) return NULL; + files[0].data_mode = 0; + files[0].size = 0; + + hkvs_iomem *io = malloc(sizeof(*io)); + if(!io) { + free(files); + return NULL; + } + io->base = (hkvs_io){ + .destroy = &hkvs_iomem_destroy, + .random = &hkvs_iomem_random, + .open = &hkvs_iomem_open, + .create = &hkvs_iomem_create, + .close = &hkvs_iomem_close, + .resize = &hkvs_iomem_resize, + .unlink = &hkvs_iomem_unlink, + }; + hkvs_write_u64(io->rand_key, rand_seed); + hkvs_write_u64(io->rand_key + 8, rand_seed); + io->rand_ctr = 0; + io->n_files = 1; + io->files = files; + return &io->base; +} diff --git a/lib/iter.c b/lib/iter.c new file mode 100644 index 0000000..ff27a68 --- /dev/null +++ b/lib/iter.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +void hkvs_iter_open(hkvs *c, hkvs_table *t, hkvs_iter *it) { + (void)c; + + it->priv1 = 0; + it->priv2 = hkvs_slab_size(&t->slab); +} + +void hkvs_iter_seek(hkvs *c, hkvs_table *t, hkvs_iter *it, hkvs_rid id) { + (void)c; + + size_t n = hkvs_slab_size(&t->slab); + it->priv1 = id.id >= n ? SIZE_MAX : (size_t)id.id; + it->priv2 = n; +} + +hkvs_rid hkvs_iter_next(hkvs *c, hkvs_table *t, hkvs_iter *it, const char **pkey) { + (void)c; + + size_t id = it->priv1; + size_t n = it->priv2; + if(id >= n) return HKVS_INVALID_RID; + while(true) { + uint8_t mark = hkvs_slab_mark(&t->slab, id); + if(mark != 0) { + it->priv1 = id + 1; + if(pkey) *pkey = hkvs_slab_at(&t->slab, id); + return (hkvs_rid){id}; + } + id++; + if(id == n) { + it->priv1 = SIZE_MAX; + return HKVS_INVALID_RID; + } + } +} + +void hkvs_iter_delete(hkvs *c, hkvs_table *t, hkvs_iter *it) { + hkvs_record_delete(c, t, (hkvs_rid){it->priv1 - 1}); +} diff --git a/lib/ix.c b/lib/ix.c new file mode 100644 index 0000000..0a6af19 --- /dev/null +++ b/lib/ix.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +static bool hkvs_ix_add_fast(char *ix, size_t id, const char *key, size_t key_size, uint8_t bits, const char *hk); + +static int hkvs_ix_build_try(hkvs_table *t, hkvs_io *io, uint8_t ix_bits) { + if(ix_bits > 59) return -EFBIG; + uint64_t ix_size_64 = HKVS_IX_SIZE(ix_bits); + if(ix_size_64 > SIZE_MAX) return -EFBIG; + size_t ix_size = (size_t)ix_size_64; + + char ix_key[HKVS_IX_KEY_SIZE]; + + int r = io->random(io, ix_key, HKVS_IX_KEY_SIZE); + if(r < 0) return r; + + char *ix; + uint64_t fi; + r = io->create(io, &fi, &ix, ix_size); + if(r < 0) return r; + + size_t key_size = hkvs_read_u8(t->slab.param + HKVS_PARAM_TABLE_KEYSZ); + size_t n = hkvs_slab_size(&t->slab); + for(size_t i = 0; i < n; i++) { + if(hkvs_slab_mark(&t->slab, i) == 0) continue; + const char *key = hkvs_slab_at(&t->slab, i); + if(!hkvs_ix_add_fast(ix, i, key, key_size, ix_bits, ix_key)) { + io->close(io, fi, ix, ix_size); + io->unlink(io, fi); + return 0; + } + } + + uint64_t old_fi = hkvs_read_u64(t->slab.param + HKVS_PARAM_TABLE_IXFI); + if(old_fi) { + if(t->ix) { + io->close(io, old_fi, t->ix, t->ix_size); + } + io->unlink(io, old_fi); + } + + t->ix = ix; + t->ix_size = ix_size; + + hkvs_write_u8(t->slab.param + HKVS_PARAM_TABLE_IXBIT, ix_bits); + hkvs_write_u64(t->slab.param + HKVS_PARAM_TABLE_IXFI, fi); + memcpy(t->slab.param + HKVS_PARAM_TABLE_IXKEY, ix_key, HKVS_IX_KEY_SIZE); + + return 1; +} + +int hkvs_ix_build(hkvs_table *t, hkvs_io *io) { + size_t count = 0; + { + size_t n = hkvs_slab_size(&t->slab); + for(size_t i = 0; i < n; i++) { + if(hkvs_slab_mark(&t->slab, i)) count++; + } + } + + uint8_t bits = 4; + if(count > 0) { + size_t load = count - 1; + while(load >= 13) { + load /= 2; + bits++; + } + } + + size_t attempts = 0; + while(true) { + int r = hkvs_ix_build_try(t, io, bits); + if(r < 0) return r; + if(r) return 0; + + attempts++; + if(attempts == 8 || attempts == 64) { + bits++; + } + } +} + +int hkvs_ix_add(hkvs *c, hkvs_table *t, size_t id) { + const char *key = hkvs_slab_at(&t->slab, id); + + char *ix = t->ix; + uint8_t bits = hkvs_table__ix_bits(t); + size_t key_size = hkvs_table__key_size(t); + const char *hk = hkvs_table__ix_key(t); + + if(hkvs_ix_add_fast(ix, id, key, key_size, bits, hk)) return 0; + + return hkvs_ix_build(t, c->io); +} + +hkvs_rid hkvs_record_find(hkvs *c, hkvs_table *t, const char *key) { + (void)c; + + const char *hk = hkvs_table__ix_key(t); + size_t key_size = hkvs_table__key_size(t); + uint64_t hv = hkvs_hash(hk, key, key_size); + + uint8_t bits = hkvs_table__ix_bits(t); + size_t entries = hkvs_slab_size(&t->slab); + char *ix = t->ix; + + size_t size = (size_t)1 << bits; + size_t mask = size - 1; + + for(size_t disp = 0; disp < HKVS_IX_MAX_DISP; disp++) { + size_t at = ((size_t)hv + disp) & mask; + char *ent = ix + at * HKVS_IX_ENTRY; + uint64_t v = hkvs_read_u64(ent); + if(v == 0) return HKVS_INVALID_RID; + if(v > entries) goto fail; + uint64_t h = hkvs_read_u64(ent + 8); + if(h != hv) { + if(((at - h) & mask) < disp) break; + continue; + } + + size_t id = (size_t)(v - 1); + char *found = hkvs_slab_at(&t->slab, id); + if(!memcmp(key, found, key_size)) { + return (hkvs_rid){id}; + } + } + +fail: + return HKVS_INVALID_RID; +} + +void hkvs_ix_remove(hkvs_table *t, size_t id) { + const char *key = hkvs_slab_at(&t->slab, id); + + uint8_t bits = hkvs_table__ix_bits(t); + const char *hk = hkvs_table__ix_key(t); + size_t key_size = hkvs_table__key_size(t); + char *ix = t->ix; + + uint64_t hv = hkvs_hash(hk, key, key_size); + size_t size = (size_t)1 << bits; + size_t mask = size - 1; + + uint64_t vv = (uint64_t)id + 1; + + char *ent; + for(size_t disp = 0; disp < HKVS_IX_MAX_DISP; disp++) { + size_t at = ((size_t)hv + disp) & mask; + ent = ix + at * HKVS_IX_ENTRY; + uint64_t v = hkvs_read_u64(ent); + if(v == vv) { + if(hkvs_read_u64(ent + 8) != hv) goto corrupted; + while(true) { + size_t next = (at + 1) & mask; + char *e2 = ix + next * HKVS_IX_ENTRY; + v = hkvs_read_u64(e2); + if(v == 0) break; + uint64_t h = hkvs_read_u64(e2 + 8); + if((h & mask) == next) break; + hkvs_write_u64(ent, v); + hkvs_write_u64(ent + 8, h); + at = next; + ent = e2; + } + hkvs_write_u64(ent, 0); + hkvs_write_u64(ent + 8, 0); + return; + } + if(v == 0) goto corrupted; + } + +corrupted: + hkvs_die("database corrupted"); +} + +bool hkvs_ix_add_fast(char *ix, size_t id, const char *key, size_t key_size, uint8_t bits, const char *hk) { + uint64_t h_new = hkvs_hash(hk, key, key_size); + size_t size = (size_t)1 << bits; + size_t mask = size - 1; + + uint64_t v_new = (uint64_t)id + 1; + + for(size_t disp = 0; disp < HKVS_IX_MAX_DISP; disp++) { + size_t at = ((size_t)h_new + disp) & mask; + char *ent = ix + at * HKVS_IX_ENTRY; + uint64_t v_disp = hkvs_read_u64(ent); + if(v_disp == 0) { + hkvs_write_u64(ent, v_new); + hkvs_write_u64(ent + 8, h_new); + return true; + } + uint64_t h_disp = hkvs_read_u64(ent + 8); + if(((at - h_disp) & mask) < disp) { + hkvs_write_u64(ent, v_new); + hkvs_write_u64(ent + 8, h_new); + while(true) { + at = (at + 1) & mask; + ent = ix + at * HKVS_IX_ENTRY; + uint64_t v2 = hkvs_read_u64(ent); + uint64_t h2 = hkvs_read_u64(ent + 8); + hkvs_write_u64(ent, v_disp); + hkvs_write_u64(ent + 8, h_disp); + if(v2 == 0) return true; + v_disp = v2; + h_disp = h2; + if(((at - h_disp) & mask) >= HKVS_IX_MAX_DISP - 1) return false; + } + return true; + } + } + + return false; +} diff --git a/lib/record.c b/lib/record.c new file mode 100644 index 0000000..c9c36d6 --- /dev/null +++ b/lib/record.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +const char *hkvs_record_key(hkvs *c, hkvs_table *t, hkvs_rid rid) { + (void)c; + + if(rid.id > hkvs_slab_size(&t->slab)) return NULL; + size_t id = (size_t)rid.id; + if(hkvs_slab_mark(&t->slab, id) == 0) return NULL; + return hkvs_slab_at(&t->slab, id); +} + +int hkvs_record_value(hkvs *c, hkvs_table *t, hkvs_rid rid, char **pvalue, size_t *psize) { + if(rid.id > hkvs_slab_size(&t->slab)) return 0; + size_t id = (size_t)rid.id; + size_t key_size = hkvs_table__key_size(t); + char *key = hkvs_slab_at(&t->slab, id); + return hkvs_record__value(c, t, id, key, key_size, 0, pvalue, psize); +} + +int hkvs_record__value(hkvs *c, hkvs_table *t, size_t id, char *key, size_t key_size, int notfound, char **pvalue, size_t *psize) { + uint8_t mark = hkvs_slab_mark(&t->slab, id); + if(mark == 0) return notfound; + + char *p = key + key_size; + size_t size; + + if(mark <= HKVS_MARK_MAX_SMALL) { + size = mark - 1; + if(!pvalue) goto ret_novalue; + goto ret_value; + } + + uint64_t size_64 = hkvs_read_u64(p); + if(size_64 > SIZE_MAX) return -EFBIG; + size = (size_t)size_64; + + if(!pvalue) goto ret_novalue; + + uint64_t val = hkvs_read_u64(p + 8); + switch(mark) { + case HKVS_MARK_EXT: + { + size_t slot; + int r = hkvs_extfile_open(c, val, &slot, size); + if(r < 0) return r; + + hkvs_extfile *f = &c->extfiles[slot]; + hkvs_extfile_pin(c, f); + p = f->addr; + break; + } + + case HKVS_MARK_SLAB: + { + uint8_t slab = val & 255; + val >>= 8; + + if(slab >= HKVS_N_SLABS) return -EBADMSG; + hkvs_slab *s = &c->slabs[slab]; + if(size > s->elt_size) return -EBADMSG; + + if(val > hkvs_slab_size(s)) return -EBADMSG; + size_t sid = (size_t)val; + + p = hkvs_slab_at(s, sid); + break; + } + + default: + return -EBADMSG; + } + +ret_value: + *pvalue = p; +ret_novalue: + if(psize) *psize = size; + return 1; +} + +int hkvs_record_resize(hkvs *c, hkvs_table *t, hkvs_rid rid, char **pvalue, size_t new_size, size_t keep_size) { + hkvs_put_records(c); + + if(rid.id > hkvs_slab_size(&t->slab)) goto wtf; + size_t id = (size_t)rid.id; + size_t key_size = hkvs_table__key_size(t); + uint8_t inl_size = hkvs_table__inl_size(t); + uint8_t mark = hkvs_slab_mark(&t->slab, id); + if(mark == 0) goto wtf; + char *key = hkvs_slab_at(&t->slab, id); + char *p = key + key_size; + + uint8_t new_slab = hkvs_slab_for_size(new_size); + + uint64_t old_fi = 0; + size_t old_fi_slot; + + hkvs_slab *old_s = NULL; + size_t old_sid; + + char *old_value; + size_t old_size; + + if(mark < HKVS_MARK_MAX_SMALL) { + if(new_size <= inl_size) { + hkvs_slab_set_mark(&t->slab, id, (uint8_t)(new_size + 1)); + if(pvalue) *pvalue = p; + return 0; + } + old_value = p; + old_size = mark - 1; + } else { + uint64_t val = hkvs_read_u64(p); + if(val > SIZE_MAX) return -EFBIG; + old_size = (size_t)val; + val = hkvs_read_u64(p + 8); + + switch(mark) { + case HKVS_MARK_EXT: + { + old_fi = val; + + int r = hkvs_extfile_open(c, old_fi, &old_fi_slot, old_size); + if(r < 0) return r; + hkvs_extfile *f = &c->extfiles[old_fi_slot]; + old_value = f->addr; + + if(new_slab >= HKVS_N_SLABS && keep_size <= HKVS_REWRITE_THRESHOLD) { + r = c->io->resize(c->io, old_fi, &f->addr, f->size, new_size); + if(r < 0) return r; + f->size = new_size; + hkvs_write_u64(p, new_size); + if(pvalue) { + hkvs_extfile_pin(c, f); + *pvalue = f->addr; + } + return 0; + } + + break; + } + + case HKVS_MARK_SLAB: + { + uint8_t slab = val & 255; + val >>= 8; + + if(slab >= HKVS_N_SLABS) return -EBADMSG; + old_s = &c->slabs[slab]; + if(old_size > old_s->elt_size) return -EBADMSG; + + if(val > hkvs_slab_size(old_s)) return -EBADMSG; + old_sid = (size_t)val; + + old_value = hkvs_slab_at(old_s, old_sid); + + if(slab == new_slab) { + hkvs_write_u64(p, new_size); + if(pvalue) *pvalue = old_value; + return 0; + } + break; + } + + default: + return -EBADMSG; + } + } + + if(old_size < keep_size) keep_size = old_size; + if(new_size < keep_size) keep_size = new_size; + + char *new_value; + + if(new_size <= inl_size) { + hkvs_slab_set_mark(&t->slab, id, (uint8_t)(new_size + 1)); + new_value = p; + } else { + new_slab = hkvs_slab_for_size(new_size); + hkvs_write_u64(p, new_size); + if(new_slab < HKVS_N_SLABS) { + hkvs_slab *s = &c->slabs[new_slab]; + size_t sid; + int r = hkvs_slab_alloc(c, s, &sid); + if(r < 0) return r; + hkvs_slab_set_mark(s, sid, 1); + new_value = hkvs_slab_at(s, sid); + hkvs_write_u64(p + 8, ((uint64_t)sid << 8) | new_slab); + hkvs_slab_set_mark(&t->slab, id, HKVS_MARK_SLAB); + } else { + char *addr; + uint64_t new_fi; + int r = hkvs_extfile_create(c, &new_fi, &addr, new_size, !!pvalue); + if(r < 0) return r; + new_value = addr; + hkvs_write_u64(p + 8, new_fi); + hkvs_slab_set_mark(&t->slab, id, HKVS_MARK_EXT); + } + } + + memcpy(new_value, old_value, keep_size); + if(pvalue) *pvalue = new_value; + + if(old_fi) { + hkvs_extfile_close(c, old_fi_slot); + c->io->unlink(c->io, old_fi); + } + + if(old_s) { + hkvs_slab_discard(old_s, old_sid); + } + + return 0; + +wtf: + hkvs_die("record does not exist"); +} + +void hkvs_record_delete(hkvs *c, hkvs_table *t, hkvs_rid rid) { + if(rid.id > hkvs_slab_size(&t->slab)) goto wtf; + size_t id = (size_t)rid.id; + size_t key_size = hkvs_table__key_size(t); + uint8_t mark = hkvs_slab_mark(&t->slab, id); + if(mark == 0) goto wtf; + char *key = hkvs_slab_at(&t->slab, id); + char *p = key + key_size; + + uint64_t unlink_fi = 0; + hkvs_slab *s = NULL; + size_t sid; + + switch(mark) { + case HKVS_MARK_SLAB: + { + uint64_t val = hkvs_read_u64(p + 8); + uint8_t slab = val & 255; + val >>= 8; + + if(slab >= HKVS_N_SLABS) goto corrupted; + s = &c->slabs[slab]; + + if(val >= hkvs_slab_size(s)) goto corrupted; + sid = (size_t)val; + break; + } + + case HKVS_MARK_EXT: + { + uint64_t fi = hkvs_read_u64(p + 8); + if(fi == 0) goto corrupted; + + size_t slot; + if(hkvs_extfile_find(c, fi, &slot)) { + hkvs_extfile_close(c, slot); + } + + unlink_fi = fi; + break; + } + + default: + break; + } + + hkvs_ix_remove(t, id); + hkvs_slab_discard(&t->slab, id); + + if(unlink_fi) { + c->io->unlink(c->io, unlink_fi); + } + + if(s) { + hkvs_slab_discard(s, sid); + } + + return; + +corrupted: + hkvs_die("database corrupted"); + +wtf: + hkvs_die("record does not exist"); +} + +int hkvs_record_append(hkvs *c, hkvs_table *t, hkvs_rid *prid, const char *key, char **pvalue, size_t size) { + size_t key_size = hkvs_table__key_size(t); + uint8_t inl_size = hkvs_table__inl_size(t); + + size_t id; + int r = hkvs_slab_alloc(c, &t->slab, &id); + if(r < 0) return r; + + char *r_key = hkvs_slab_at(&t->slab, id); + memcpy(r_key, key, key_size); + + char *p = r_key + key_size; + char *mark = hkvs_slab_mark_at(&t->slab, id); + + hkvs_slab *s = NULL; + size_t sid; + char *value; + + uint64_t ext_fi = 0; + + if(size <= inl_size) { + hkvs_write_u8(mark, (uint8_t)(size + 1)); + value = p; + } else { + uint8_t slab = hkvs_slab_for_size(size); + hkvs_write_u64(p, size); + if(slab < HKVS_N_SLABS) { + s = &c->slabs[slab]; + r = hkvs_slab_alloc(c, s, &sid); + if(r < 0) return r; + value = hkvs_slab_at(s, sid); + hkvs_slab_set_mark(s, sid, 1); + + hkvs_write_u64(p + 8, (uint64_t)sid << 8 | slab); + hkvs_write_u8(mark, HKVS_MARK_SLAB); + } else { + char *addr; + r = hkvs_extfile_create(c, &ext_fi, &addr, size, !!pvalue); + value = addr; + + hkvs_write_u64(p + 8, ext_fi); + hkvs_write_u8(mark, HKVS_MARK_EXT); + } + } + + r = hkvs_ix_add(c, t, id); + if(r >= 0) { + if(prid) *prid = (hkvs_rid){id}; + if(pvalue) *pvalue = value; + return 1; + } + + hkvs_write_u8(mark, 0); + + if(s) { + hkvs_slab_discard(s, sid); + } + + if(ext_fi) { + size_t slot = --c->n_extfiles; + hkvs_io *io = c->io; + io->close(io, ext_fi, c->extfiles[slot].addr, c->extfiles[slot].size); + io->unlink(io, ext_fi); + } + + return r; +} diff --git a/lib/slab.c b/lib/slab.c new file mode 100644 index 0000000..2d2d8e1 --- /dev/null +++ b/lib/slab.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" +#include <string.h> +#include <stdlib.h> + +static size_t hkvs_slab_size_to_count(size_t elt_size, size_t size) { + size_t block_size = (elt_size + 1) * HKVS_SLAB_BLOCK; + + size_t blocks = size / block_size; + size %= block_size; + size_t rest = 0; + if(size > HKVS_SLAB_BLOCK) { + rest = (size - HKVS_SLAB_BLOCK) / elt_size; + } + + return blocks * HKVS_SLAB_BLOCK + rest; +} + +static size_t hkvs_slab_count_to_size(size_t elt_size, size_t count) { + size_t size; + if(__builtin_mul_overflow(count, elt_size + 1, &size)) return SIZE_MAX; + if(__builtin_add_overflow(size, (-count) % HKVS_SLAB_BLOCK, &size)) return SIZE_MAX; + return size; +} + +int hkvs_slab_init(hkvs_slab *s, hkvs_io *io, char *param, size_t elt_size) { + s->param = param; + s->elt_size = elt_size; + + uint64_t fi = hkvs_read_u64(param + HKVS_PARAM_SLAB_FI); + if(fi == 0) { + s->addr = NULL; + s->map_size = 0; + return 0; + } + + int r = io->open(io, fi, &s->addr, &s->map_size); + if(r < 0) return r; + + uint64_t alloc_count = hkvs_read_u64(param + HKVS_PARAM_SLAB_ALLOC); + if(alloc_count > hkvs_slab_size_to_count(elt_size, s->map_size)) { + io->close(io, fi, s->addr, s->map_size); + return -EBADMSG; + } + + return 0; +} + +int hkvs_slab_alloc(hkvs *c, hkvs_slab *s, size_t *pi) { + size_t alloc_count = hkvs_slab_size(s); + + uint64_t at_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE); + if(at_64 < alloc_count) { + size_t at = (size_t)at_64; + + char *p = hkvs_slab_at(s, at); + + for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { + uint64_t v = hkvs_read_u64(p + i * 8); + if(v <= at) return -EBADMSG; + if(v < alloc_count) { + size_t id = (size_t)v; + hkvs_write_u64(p + i * 8, UINT64_MAX); + *pi = id; + return 0; + } + } + + at_64 = hkvs_read_u64(p); + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, at_64); + + *pi = at; + return 0; + } + + if(at_64 != UINT64_MAX) { + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, UINT64_MAX); + } + + size_t cap_count = hkvs_slab_size_to_count(s->elt_size, s->map_size); + hkvs_assume(alloc_count <= cap_count); + if(alloc_count == cap_count) { + size_t grow_count = cap_count + (cap_count >> 3) + 1; + size_t grow_size; + while(true) { + grow_size = hkvs_slab_count_to_size(s->elt_size, grow_count); + if(grow_size != 0) break; + size_t dec = (grow_count - cap_count) / 2; + if(dec == 0) return -EFBIG; + grow_count -= dec; + } + + hkvs_io *io = c->io; + uint64_t fi = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FI); + if(fi == 0) { + int r = io->create(io, &fi, &s->addr, grow_size); + if(r < 0) return r; + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FI, fi); + } else { + int r = io->resize(io, fi, &s->addr, s->map_size, grow_size); + if(r < 0) return r; + } + s->map_size = grow_size; + } + + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count + 1); + *pi = alloc_count; + return 0; +} + +void hkvs_slab_discard(hkvs_slab *s, size_t id) { + size_t alloc_count = hkvs_slab_size(s); + hkvs_assume(id < alloc_count); + if(id == alloc_count - 1) { + do { + alloc_count--; + } while(alloc_count > 0 && hkvs_slab_mark(s, alloc_count - 1) == 0); + + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count); + return; + } + + hkvs_slab_set_mark(s, id, 0); + + uint64_t free_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE); + if(id < free_64) { + hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, id); + char *p = hkvs_slab_at(s, id); + hkvs_write_u64(p, free_64); + for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { + hkvs_write_u64(p + i * 8, UINT64_MAX); + } + return; + } + + char *p = hkvs_slab_at(s, (size_t)free_64); + uint64_t v = hkvs_read_u64(p + 8); + + if(v >= alloc_count) { + size_t i = 2; + while(true) { + v = hkvs_read_u64(p + i * 8); + if(v < alloc_count) break; + i++; + if(i == HKVS_SLAB_FREEPTRS) { + hkvs_write_u64(p + 8, id); + return; + } + } + while(v < id) { + hkvs_write_u64(p + (i - 1) * 8, v); + i++; + if(i == HKVS_SLAB_FREEPTRS) break; + v = hkvs_read_u64(p + i * 8); + } + hkvs_write_u64(p + (i - 1) * 8, id); + return; + } + + if(hkvs_read_u64(p + (HKVS_SLAB_FREEPTRS - 1) * 8) >= alloc_count) { + size_t i = 1; + while(v < id) { + i++; + v = hkvs_read_u64(p + i * 8); + } + uint64_t v2 = id; + while(true) { + hkvs_write_u64(p + i * 8, v2); + if(v >= alloc_count) break; + v2 = v; + i++; + v = hkvs_read_u64(p + i * 8); + } + return; + } + + if(v < id) { + uint64_t v2 = id; + id = (size_t)v; + size_t i = 2; + do { + v = hkvs_read_u64(p + i * 8); + if(v >= v2) break; + hkvs_write_u64(p + (i - 1) * 8, v); + i++; + } while(i < HKVS_SLAB_FREEPTRS); + hkvs_write_u64(p + (i - 1) * 8, v2); + } + + char *p2 = hkvs_slab_at(s, id); + memcpy(p2, p, HKVS_SLAB_FREEPTRS * 8); + + hkvs_write_u64(p, id); + for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { + hkvs_write_u64(p + i * 8, UINT64_MAX); + } + return; +} diff --git a/lib/table.c b/lib/table.c new file mode 100644 index 0000000..331a865 --- /dev/null +++ b/lib/table.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +int hkvs_table_new(hkvs_table **pt, hkvs_io *io, char *param) { + size_t elt_size = hkvs_read_u16(param + HKVS_PARAM_TABLE_ELTSZ); + if(!elt_size) { + *pt = NULL; + return 0; + } + + size_t key_size = hkvs_read_u8(param + HKVS_PARAM_TABLE_KEYSZ); + if(key_size > elt_size) return -EBADMSG; + + hkvs_table *t = malloc(sizeof(hkvs_table)); + if(!t) return -ENOMEM; + + int r = hkvs_slab_init(&t->slab, io, param, elt_size); + if(r < 0) goto fail_free; + + uint64_t ix_fi = hkvs_read_u64(param + HKVS_PARAM_TABLE_IXFI); + if(ix_fi != 0) { + uint8_t ix_bits = hkvs_read_u8(param + HKVS_PARAM_TABLE_IXBIT); + if(ix_bits < 4 || ix_bits > 59) goto ix_unlink; + + r = io->open(io, ix_fi, &t->ix, &t->ix_size); + if(r < 0) { + if(r == -ENOENT) goto ix_dropped; + return r; + } + if(t->ix_size < HKVS_IX_SIZE(ix_bits)) { + goto ix_build; + } + } + +ret: + *pt = t; + return 1; + +ix_unlink: + io->unlink(io, ix_fi); + +ix_dropped: + hkvs_write_u64(param + HKVS_PARAM_TABLE_IXFI, 0); + +ix_build: + r = hkvs_ix_build(t, io); + if(r < 0) goto fail_close; + + goto ret; + +fail_close: + hkvs_slab_destroy(&t->slab, io); + +fail_free: + free(t); + return r; +} + +hkvs_table *hkvs_table_at(hkvs *c, size_t i) { + size_t n_tables = c->n_tables; + if(i >= n_tables) return NULL; + + hkvs_table **tables = c->tables; + for(size_t j = 0; j < n_tables; j++) { + hkvs_table *t = tables[j]; + if(!t) continue; + if(!i--) return t; + } + return NULL; +} + +uint32_t hkvs_table_id(hkvs *c, hkvs_table *t) { + (void)c; + return hkvs_table__id(t); +} + +size_t hkvs_table_key_size(hkvs *c, hkvs_table *t) { + (void)c; + return hkvs_table__key_size(t); +} + +int hkvs_table_delete(hkvs *c, uint32_t id) { + hkvs_table **tables = c->tables; + size_t n_tables = c->n_tables; + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(!t) continue; + if(hkvs_table_id(c, t) == id) { + tables[i] = NULL; + + char *param = t->slab.param; + hkvs_io *io = c->io; + + hkvs_table_free(t, io); + + uint64_t fi = hkvs_read_u64(param + HKVS_PARAM_SLAB_FI); + uint64_t ix_fi = hkvs_read_u64(param + HKVS_PARAM_TABLE_IXFI); + memset(param, 0, HKVS_PSIZE_TABLE); + + assert(fi && ix_fi); + + io->unlink(io, ix_fi); + io->unlink(io, fi); + return 1; + } + } + return 0; +} + +hkvs_table *hkvs_table_open(hkvs *c, uint32_t id, size_t key_size) { + hkvs_table **tables = c->tables; + size_t n_tables = c->n_tables; + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(!t) continue; + if(hkvs_table__id(t) == id) { + if(hkvs_table__key_size(t) != key_size) return NULL; + return t; + } + } + return NULL; +} + +HKVS_INLINE size_t hkvs_elt_size(size_t key_size, size_t val_size_hint) { + size_t elt_size; + + if(val_size_hint >= 16 && val_size_hint <= 248) { + elt_size = key_size + val_size_hint; + elt_size += (-elt_size) % 64; + if(elt_size - key_size <= 248) return elt_size; + } + + elt_size = key_size + 16; + elt_size += (-elt_size) % 64; + return elt_size; +} + + +int hkvs_table_create(hkvs *c, hkvs_table **pt, uint32_t id, size_t key_size_z, size_t val_size_hint) { + hkvs_table **tables = c->tables; + size_t n_tables = c->n_tables; + for(size_t i = 0; i < n_tables; i++) { + hkvs_table *t = tables[i]; + if(!t) continue; + if(hkvs_table__id(t) == id) { + if(hkvs_table__key_size(t) != key_size_z) return -EEXIST; + *pt = t; + return 0; + } + } + + if(key_size_z > 255) return -EINVAL; + uint8_t key_size = (uint8_t)key_size_z; + size_t elt_size = hkvs_elt_size(key_size, val_size_hint); + + size_t slot; + for(slot = 0; slot < n_tables; slot++) { + if(!tables[slot]) break; + } + if(slot == n_tables) { + if(c->alloc_tables == n_tables) { + size_t new_alloc = c->alloc_tables; + new_alloc += (new_alloc >> 2) + 1; + tables = realloc(tables, new_alloc * sizeof(hkvs_table*)); + if(!tables) return -ENOMEM; + c->tables = tables; + c->alloc_tables = new_alloc; + } + + c->tables[n_tables++] = NULL; + c->n_tables = n_tables; + int r = hkvs_resize_param(c); + if(r < 0) { + c->n_tables = --n_tables; + return r; + } + } + + char *param = c->root_param + HKVS_POFF_TABLE + slot * HKVS_PSIZE_TABLE; + memset(param, 0, HKVS_PSIZE_TABLE); + + hkvs_table *t = malloc(sizeof(hkvs_table)); + if(!t) return -ENOMEM; + + hkvs_write_u16(param + HKVS_PARAM_TABLE_ELTSZ, (uint16_t)elt_size); + hkvs_write_u8(param + HKVS_PARAM_TABLE_KEYSZ, key_size); + hkvs_write_u32(param + HKVS_PARAM_TABLE_ID, id); + + hkvs_io *io = c->io; + + int r = hkvs_slab_init(&t->slab, io, param, elt_size); + if(r < 0) goto fail_free; + + r = hkvs_ix_build(t, io); + if(r < 0) goto fail_close; + + c->tables[slot] = t; + + *pt = t; + return 1; + +fail_close: + hkvs_slab_destroy(&t->slab, io); + + uint64_t fi = hkvs_read_u64(param + HKVS_PARAM_SLAB_FI); + io->unlink(io, fi); + +fail_free: + free(t); + return r; +} diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 0000000..560671c --- /dev/null +++ b/lib/util.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl.h" + +int hkvs_record_set(hkvs *c, hkvs_table *t, hkvs_rid rid, const char *value, size_t size) { + char *ptr; + int r = hkvs_record_resize(c, t, rid, &ptr, size, 0); + if(r < 0) return r; + memcpy(ptr, value, size); + hkvs_put_records(c); + return 0; +} + +int hkvs_record_update(hkvs *c, hkvs_table *t, hkvs_rid *prid, const char *key, const char *value, size_t size) { + hkvs_rid rid = hkvs_record_find(c, t, key); + if(hkvs_rid_ok(rid)) { + int r = hkvs_record_set(c, t, rid, value, size); + if(r < 0) return 0; + *prid = rid; + return 0; + } else { + char *ptr; + int r = hkvs_record_append(c, t, &rid, key, &ptr, size); + if(r < 0) return r; + memcpy(ptr, value, size); + hkvs_put_records(c); + *prid = rid; + return 1; + } +} |