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