diff options
Diffstat (limited to 'lib/db.c')
-rw-r--r-- | lib/db.c | 180 |
1 files changed, 180 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; +} |