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