aboutsummaryrefslogtreecommitdiff
path: root/lib/extfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/extfile.c')
-rw-r--r--lib/extfile.c150
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);
+ }
+}