aboutsummaryrefslogblamecommitdiff
path: root/lib/slab.c
blob: 2d2d8e12451763c1646bcc38c19f75a5239dfc67 (plain) (tree)






































































































































































































                                                                                            
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "impl.h"
#include <string.h>
#include <stdlib.h>

static size_t hkvs_slab_size_to_count(size_t elt_size, size_t size) {
	size_t block_size = (elt_size + 1) * HKVS_SLAB_BLOCK;

	size_t blocks = size / block_size;
	size %= block_size;
	size_t rest = 0;
	if(size > HKVS_SLAB_BLOCK) {
		rest = (size - HKVS_SLAB_BLOCK) / elt_size;
	}

	return blocks * HKVS_SLAB_BLOCK + rest;
}

static size_t hkvs_slab_count_to_size(size_t elt_size, size_t count) {
	size_t size;
	if(__builtin_mul_overflow(count, elt_size + 1, &size)) return SIZE_MAX;
	if(__builtin_add_overflow(size, (-count) % HKVS_SLAB_BLOCK, &size)) return SIZE_MAX;
	return size;
}

int hkvs_slab_init(hkvs_slab *s, hkvs_io *io, char *param, size_t elt_size) {
	s->param = param;
	s->elt_size = elt_size;

	uint64_t fi = hkvs_read_u64(param + HKVS_PARAM_SLAB_FI);
	if(fi == 0) {
		s->addr = NULL;
		s->map_size = 0;
		return 0;
	}

	int r = io->open(io, fi, &s->addr, &s->map_size);
	if(r < 0) return r;

	uint64_t alloc_count = hkvs_read_u64(param + HKVS_PARAM_SLAB_ALLOC);
	if(alloc_count > hkvs_slab_size_to_count(elt_size, s->map_size)) {
		io->close(io, fi, s->addr, s->map_size);
		return -EBADMSG;
	}

	return 0;
}

int hkvs_slab_alloc(hkvs *c, hkvs_slab *s, size_t *pi) {
	size_t alloc_count = hkvs_slab_size(s);

	uint64_t at_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE);
	if(at_64 < alloc_count) {
		size_t at = (size_t)at_64;

		char *p = hkvs_slab_at(s, at);

		for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) {
			uint64_t v = hkvs_read_u64(p + i * 8);
			if(v <= at) return -EBADMSG;
			if(v < alloc_count) {
				size_t id = (size_t)v;
				hkvs_write_u64(p + i * 8, UINT64_MAX);
				*pi = id;
				return 0;
			}
		}

		at_64 = hkvs_read_u64(p);
		hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, at_64);

		*pi = at;
		return 0;
	}

	if(at_64 != UINT64_MAX) {
		hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, UINT64_MAX);
	}

	size_t cap_count = hkvs_slab_size_to_count(s->elt_size, s->map_size);
	hkvs_assume(alloc_count <= cap_count);
	if(alloc_count == cap_count) {
		size_t grow_count = cap_count + (cap_count >> 3) + 1;
		size_t grow_size;
		while(true) {
			grow_size = hkvs_slab_count_to_size(s->elt_size, grow_count);
			if(grow_size != 0) break;
			size_t dec = (grow_count - cap_count) / 2;
			if(dec == 0) return -EFBIG;
			grow_count -= dec;
		}

		hkvs_io *io = c->io;
		uint64_t fi = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FI);
		if(fi == 0) {
			int r = io->create(io, &fi, &s->addr, grow_size);
			if(r < 0) return r;
			hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FI, fi);
		} else {
			int r = io->resize(io, fi, &s->addr, s->map_size, grow_size);
			if(r < 0) return r;
		}
		s->map_size = grow_size;
	}

	hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count + 1);
	*pi = alloc_count;
	return 0;
}

void hkvs_slab_discard(hkvs_slab *s, size_t id) {
	size_t alloc_count = hkvs_slab_size(s);
	hkvs_assume(id < alloc_count);
	if(id == alloc_count - 1) {
		do {
			alloc_count--;
		} while(alloc_count > 0 && hkvs_slab_mark(s, alloc_count - 1) == 0);

		hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count);
		return;
	}

	hkvs_slab_set_mark(s, id, 0);

	uint64_t free_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE);
	if(id < free_64) {
		hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, id);
		char *p = hkvs_slab_at(s, id);
		hkvs_write_u64(p, free_64);
		for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) {
			hkvs_write_u64(p + i * 8, UINT64_MAX);
		}
		return;
	}

	char *p = hkvs_slab_at(s, (size_t)free_64);
	uint64_t v = hkvs_read_u64(p + 8);

	if(v >= alloc_count) {
		size_t i = 2;
		while(true) {
			v = hkvs_read_u64(p + i * 8);
			if(v < alloc_count) break;
			i++;
			if(i == HKVS_SLAB_FREEPTRS) {
				hkvs_write_u64(p + 8, id);
				return;
			}
		}
		while(v < id) {
			hkvs_write_u64(p + (i - 1) * 8, v);
			i++;
			if(i == HKVS_SLAB_FREEPTRS) break;
			v = hkvs_read_u64(p + i * 8);
		}
		hkvs_write_u64(p + (i - 1) * 8, id);
		return;
	}

	if(hkvs_read_u64(p + (HKVS_SLAB_FREEPTRS - 1) * 8) >= alloc_count) {
		size_t i = 1;
		while(v < id) {
			i++;
			v = hkvs_read_u64(p + i * 8);
		}
		uint64_t v2 = id;
		while(true) {
			hkvs_write_u64(p + i * 8, v2);
			if(v >= alloc_count) break;
			v2 = v;
			i++;
			v = hkvs_read_u64(p + i * 8);
		}
		return;
	}

	if(v < id) {
		uint64_t v2 = id;
		id = (size_t)v;
		size_t i = 2;
		do {
			v = hkvs_read_u64(p + i * 8);
			if(v >= v2) break;
			hkvs_write_u64(p + (i - 1) * 8, v);
			i++;
		} while(i < HKVS_SLAB_FREEPTRS);
		hkvs_write_u64(p + (i - 1) * 8, v2);
	}

	char *p2 = hkvs_slab_at(s, id);
	memcpy(p2, p, HKVS_SLAB_FREEPTRS * 8);

	hkvs_write_u64(p, id);
	for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) {
		hkvs_write_u64(p + i * 8, UINT64_MAX);
	}
	return;
}