aboutsummaryrefslogblamecommitdiff
path: root/lib/io_mem.c
blob: 4f27c0222acbe5352de72a90515d5e479a0c3dbb (plain) (tree)




















































































































































































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