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/extfile.c |
Diffstat (limited to 'lib/extfile.c')
-rw-r--r-- | lib/extfile.c | 150 |
1 files changed, 150 insertions, 0 deletions
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); + } +} |