// SPDX-License-Identifier: LGPL-3.0-or-later
#include "internal.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
typedef struct hkvs_iomem_file hkvs_iomem_file;
struct hkvs_iomem_file {
uintptr_t data_mode;
size_t size;
};
typedef struct hkvs_iomem hkvs_iomem;
struct hkvs_iomem {
hkvs_io base;
uint64_t rand_ctr;
char rand_key[16];
size_t n_files;
struct hkvs_iomem_file *files;
};
static void hkvs_iomem_destroy(hkvs_io *iov) {
hkvs_iomem *io = (hkvs_iomem*)iov;
size_t n = io->n_files;
for(size_t i = 0; i < n; i++) {
struct hkvs_iomem_file *f = io->files + i;
uintptr_t mode = f->data_mode & 3;
char *p = (char*)(f->data_mode - mode);
free(p);
}
free(io->files);
free(io);
}
static int hkvs_iomem_random(hkvs_io *iov, char *buf, size_t n) {
hkvs_iomem *io = (hkvs_iomem*)iov;
while(true) {
uint64_t h = hkvs_hash(io->rand_key, (const char*)&io->rand_ctr, sizeof(io->rand_ctr));
io->rand_ctr++;
if(n > 8) {
memcpy(buf, &h, 8);
buf += 8;
n -= 8;
} else {
memcpy(buf, &h, n);
return 0;
}
}
}
static int hkvs_iomem_open(hkvs_io *iov, uint64_t fi, char **pmem, size_t *psz) {
hkvs_iomem *io = (hkvs_iomem*)iov;
if(fi >= io->n_files) return -ENOENT;
hkvs_iomem_file *f = io->files + (size_t)fi;
uintptr_t mode = f->data_mode & 3;
char *p = (char*)(f->data_mode - mode);
if(mode == 0) {
if(fi != 0) return -ENOENT;
} else if(mode != 1) {
hkvs_die("bad file state");
}
f->data_mode = (uintptr_t)p + 2;
*pmem = p;
*psz = f->size;
return 0;
}
static int hkvs_iomem_create(hkvs_io *iov, uint64_t *pfi, char **pmem, size_t sz) {
hkvs_iomem *io = (hkvs_iomem*)iov;
hkvs_iomem_file *f = io->files;
size_t n = io->n_files;
for(size_t i = 1; i < n; i++) {
if(!f[i].data_mode) {
n = i;
f += n;
goto done;
}
}
f = realloc(f, (n + 1) * sizeof(hkvs_iomem_file));
if(!f) return -ENOMEM;
io->files = f;
io->n_files = n + 1;
f += n;
f->data_mode = 0;
done:;
char *p = malloc(sz);
if(!p) return -ENOMEM;
memset(p, 0, sz);
f->data_mode = (uintptr_t)p + 2;
f->size = sz;
*pfi = n;
*pmem = p;
return 0;
}
static int hkvs_iomem_resize(hkvs_io *iov, uint64_t fi, char **pmem, size_t old_sz, size_t new_sz) {
hkvs_iomem *io = (hkvs_iomem*)iov;
if(fi > io->n_files) hkvs_die("bad file index");
hkvs_iomem_file *f = io->files + (size_t)fi;
uintptr_t mode = f->data_mode & 3;
char *p = (char*)(f->data_mode - mode);
if(mode != 2) hkvs_die("bad file state");
if(*pmem != p) hkvs_die("bad file mem");
if(old_sz != f->size) hkvs_die("bad file old_size");
p = realloc(p, new_sz);
if(new_sz && !p) return -ENOMEM;
f->data_mode = (uintptr_t)p + 2;
f->size = new_sz;
*pmem = p;
return 0;
}
static void hkvs_iomem_close(hkvs_io *iov, uint64_t fi, char *mem, size_t sz) {
hkvs_iomem *io = (hkvs_iomem*)iov;
if(fi > io->n_files) hkvs_die("bad file index");
hkvs_iomem_file *f = io->files + (size_t)fi;
uintptr_t mode = f->data_mode & 3;
char *p = (char*)(f->data_mode - mode);
if(mode != 2) hkvs_die("bad file state");
if(mem != p) hkvs_die("bad file mem");
if(sz != f->size) hkvs_die("bad file size");
f->data_mode = (uintptr_t)p + 1;
}
static void hkvs_iomem_unlink(hkvs_io *iov, uint64_t fi) {
hkvs_iomem *io = (hkvs_iomem*)iov;
if(fi > io->n_files) hkvs_die("bad file index");
hkvs_iomem_file *f = io->files + (size_t)fi;
uintptr_t mode = f->data_mode & 3;
char *p = (char*)(f->data_mode - mode);
if(mode != 1) hkvs_die("bad file state");
free(p);
f->data_mode = 0;
f->size = 0;
}
hkvs_io *hkvs_io_new_mem(uint64_t rand_seed) {
hkvs_iomem_file *files = malloc(sizeof(hkvs_iomem_file));
if(!files) return NULL;
files[0].data_mode = 0;
files[0].size = 0;
hkvs_iomem *io = malloc(sizeof(*io));
if(!io) {
free(files);
return NULL;
}
io->base = (hkvs_io){
.destroy = &hkvs_iomem_destroy,
.random = &hkvs_iomem_random,
.open = &hkvs_iomem_open,
.create = &hkvs_iomem_create,
.close = &hkvs_iomem_close,
.resize = &hkvs_iomem_resize,
.unlink = &hkvs_iomem_unlink,
};
hkvs_write_u64(io->rand_key, rand_seed);
hkvs_write_u64(io->rand_key + 8, rand_seed);
io->rand_ctr = 0;
io->n_files = 1;
io->files = files;
return &io->base;
}