aboutsummaryrefslogblamecommitdiff
path: root/lib/db.c
blob: 81cce4718c79be3c33d38887735185da021a744b (plain) (tree)



















































































































































































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