// 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;
}