about summary refs log blame commit diff stats
path: root/pool.c
blob: d0cee9b305fd224c2da44187cce4446beae03b42 (plain) (tree)



































































































































































                                                                          
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "pool.h"

/* the actual pool of memory */
static void *pool;
/* size of one block in the pool */
static size_t block_size;
/* number of blocks in the pool */
static size_t blocks;

/* bitmap to track allocated blocks */
static uint64_t *used;
/* number of bits in each slot of `used` */
static const size_t BITS = sizeof(*used) * CHAR_BIT;

/* Allocate a bitmap with at least `blocks` bits. */
static uint64_t *
bitmap(size_t blocks)
{
	assert(blocks > 0);

	const size_t slots = (blocks / BITS) + 1;

	uint64_t *p = calloc(1, sizeof(*p) * slots);
	return p; /* this is fine */
}

/* Mark given `block` as free. */
static void
bitmap_free(size_t block)
{
	assert(block < blocks);

	const size_t slot = (block / BITS);
	const size_t bit = (block % BITS);

	used[slot] &= ~(1UL << bit);
}

/* Mark given `block` as used. */
static void
bitmap_used(size_t block)
{
	assert(block < blocks);

	const size_t slot = (block / BITS);
	const size_t bit = (block % BITS);

	used[slot] |= (1UL << bit);
}

/*
 * Find right-most zero bit. Returns 0 if there is none. Examples:
 *
 * 00000000	01010011	11111111	bits
 *
 * 11111111	10101100	00000000	~bits
 * 00000001	01010100	00000000	bits+1
 * --------	--------	--------
 * 00000001	00000100	00000000	~bits & (bits+1)
 */
static inline uint64_t
find_zero(const uint64_t bits)
{
	return ~bits & (bits + 1);
}

static inline uint64_t
log2(const uint64_t x)
{
	return ((BITS - 1) - __builtin_clzl(x)); /* count leading zeros */
}

/* Find a free block. Returns -1 if there is none. */
static int
bitmap_alloc(void)
{
	for (size_t i = 0; i < blocks / BITS; i++) {
		uint64_t free = find_zero(used[i]);
		if (free != 0) {
			return i * BITS + log2(free);
		}
	}
	return -1;
}

int
pl_setup(size_t object_size, size_t pool_size)
{
	assert(object_size > 0 && pool_size > 0);

	void *p = calloc(pool_size, object_size);
	if (p == NULL) {
		return -1;
	}

	uint64_t *q = bitmap(pool_size);
	if (q == NULL) {
		free(p);
		return -2;
	}

	pool = p;
	used = q;

	block_size = object_size;
	blocks = pool_size;

	return 0;
}

void *
pl_alloc(void)
{
	assert(pool);

	int block = bitmap_alloc();
	if (block < 0) {
		return NULL;
	}

	bitmap_used(block);

	const uintptr_t p = (uintptr_t)pool;
	const uintptr_t o = p + block * block_size;

	return (void *)o;
}

void
pl_free(void *object)
{
	assert(pool);

	if (object == NULL) {
		/* free(3) also does nothing for NULL */
		return;
	}

	/* memset here to catch dangling references */
	memset(object, 0, block_size);

	const uintptr_t o = (uintptr_t)object;
	const uintptr_t p = (uintptr_t)pool;
	const uintptr_t offset = o - p;
	const size_t block = offset / block_size;

	bitmap_free(block);
}

void
pl_cleanup(void)
{
	assert(pool);

	free(used);
	free(pool);
	pool = NULL;
}