aboutsummaryrefslogblamecommitdiff
path: root/lib/extfile.c
blob: fdd94ebf301134ceccd1bc9ce8cb942ad28da37a (plain) (tree)





















































































































































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