Add arena allocator
This commit is contained in:
@@ -11,6 +11,7 @@ src_dir = "src/"
|
|||||||
|
|
||||||
program_sources = ["src/main.c"]
|
program_sources = ["src/main.c"]
|
||||||
lib_srcs = [
|
lib_srcs = [
|
||||||
|
"src/arena.c",
|
||||||
"src/builtins.c",
|
"src/builtins.c",
|
||||||
"src/evaluate.c",
|
"src/evaluate.c",
|
||||||
"src/memory.c",
|
"src/memory.c",
|
||||||
@@ -23,6 +24,7 @@ lib_includes = ["src/"]
|
|||||||
|
|
||||||
|
|
||||||
test_srcs = [
|
test_srcs = [
|
||||||
|
"test/test_arena.c",
|
||||||
"test/test_e2e.c",
|
"test/test_e2e.c",
|
||||||
"test/test_parse.c",
|
"test/test_parse.c",
|
||||||
"test/test_state.c",
|
"test/test_state.c",
|
||||||
|
|||||||
101
src/arena.c
Normal file
101
src/arena.c
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct ucl_arena {
|
||||||
|
size_t object_size;
|
||||||
|
size_t capacity;
|
||||||
|
void *objects;
|
||||||
|
int *used_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DIV_ROUND_UP(numerator, denominator) \
|
||||||
|
(((numerator) + (denominator) - 1) / (denominator))
|
||||||
|
|
||||||
|
#define INT_BITS (CHAR_BIT * sizeof(int))
|
||||||
|
|
||||||
|
struct ucl_arena *ucl_arena_create(size_t object_size, size_t capacity) {
|
||||||
|
struct ucl_arena *arena = malloc(sizeof(*arena));
|
||||||
|
size_t used_map_size = DIV_ROUND_UP(capacity, INT_BITS) * sizeof(int);
|
||||||
|
|
||||||
|
arena->object_size = object_size;
|
||||||
|
arena->capacity = capacity;
|
||||||
|
arena->objects = malloc(capacity * object_size);
|
||||||
|
arena->used_map = malloc(used_map_size);
|
||||||
|
|
||||||
|
memset(arena->used_map, 0, used_map_size);
|
||||||
|
|
||||||
|
assert(arena->objects != NULL);
|
||||||
|
assert(arena->used_map != NULL);
|
||||||
|
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ucl_arena_map(struct ucl_arena *arena, void (*map_function)(struct ucl_arena * arena, void *object)) {
|
||||||
|
size_t used_map_ints = DIV_ROUND_UP(arena->capacity, INT_BITS);
|
||||||
|
|
||||||
|
for (int i = 0; i < used_map_ints; i++ ) {
|
||||||
|
// TODO: Allow for 'put' in map
|
||||||
|
int map = arena->used_map[i];
|
||||||
|
while (map) {
|
||||||
|
int bit_index = __builtin_ffs(map) - 1;
|
||||||
|
int index = bit_index + INT_BITS * i;
|
||||||
|
void *obj = arena->objects + (index * arena->object_size);
|
||||||
|
map_function(arena, obj);
|
||||||
|
map &= ~(1 << bit_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int total_arena_gets = 0;
|
||||||
|
|
||||||
|
void *ucl_arena_get(struct ucl_arena *arena) {
|
||||||
|
int used_map_ints = DIV_ROUND_UP(arena->capacity, INT_BITS);
|
||||||
|
for (int i = 0; i < used_map_ints; i++ ) {
|
||||||
|
// TODO: Maybe keep a cache of available used_map_ints
|
||||||
|
int map = arena->used_map[i];
|
||||||
|
if (!(~map)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bit_index = __builtin_ffs(~map) - 1;
|
||||||
|
int index = bit_index + INT_BITS * i;
|
||||||
|
if (index >= arena->capacity) {
|
||||||
|
// This might happen in the last used_map_int when (capacity % int_bits != 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
arena->used_map[i] |= 1 << bit_index;
|
||||||
|
total_arena_gets++;
|
||||||
|
return arena->objects + (index * arena->object_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int total_arena_puts = 0;
|
||||||
|
|
||||||
|
void ucl_arena_put(struct ucl_arena *arena, void *object) {
|
||||||
|
|
||||||
|
if (object == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t offset = object - arena->objects;
|
||||||
|
unsigned int index = offset / arena->object_size;
|
||||||
|
unsigned int int_index = index / INT_BITS;
|
||||||
|
unsigned int bit_index = index % INT_BITS;
|
||||||
|
|
||||||
|
assert(offset % arena->object_size == 0);
|
||||||
|
assert(index >= 0);
|
||||||
|
assert(index < arena->capacity);
|
||||||
|
|
||||||
|
assert(arena->used_map[int_index] & (1 << bit_index));
|
||||||
|
//if (arena->used_map[int_index] & (1 << bit_index)) {
|
||||||
|
total_arena_puts++;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
arena->used_map[int_index] &= ~(1 << bit_index);
|
||||||
|
}
|
||||||
11
src/arena.h
Normal file
11
src/arena.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _UCLISP_ARENA_H_
|
||||||
|
#define _UCLISP_ARENA_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct ucl_arena *ucl_arena_create(size_t object_size, size_t capacity);
|
||||||
|
void *ucl_arena_get(struct ucl_arena *arena);
|
||||||
|
void ucl_arena_put(struct ucl_arena *arena, void *object);
|
||||||
|
void ucl_arena_map(struct ucl_arena *arena, void (*map_function)(struct ucl_arena *arena, void *object));
|
||||||
|
|
||||||
|
#endif
|
||||||
76
test/test_arena.c
Normal file
76
test/test_arena.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <unity.h>
|
||||||
|
|
||||||
|
#include "arena.h"
|
||||||
|
|
||||||
|
#define NUM_OBJECTS 8
|
||||||
|
|
||||||
|
struct test_5 {
|
||||||
|
uint64_t value;
|
||||||
|
uint8_t pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ucl_arena *arena = NULL;
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
arena = ucl_arena_create(sizeof(struct test_5), NUM_OBJECTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_all(struct ucl_arena *arena, void *object) {
|
||||||
|
ucl_arena_put(arena, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
ucl_arena_map(arena, put_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_arena_single(void) {
|
||||||
|
struct test_5 *object = ucl_arena_get(arena);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_NULL(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_arena_get_all(void) {
|
||||||
|
struct test_5 *objects[NUM_OBJECTS] = {};
|
||||||
|
for (int i = 0; i < NUM_OBJECTS; i++) {
|
||||||
|
objects[i] = ucl_arena_get(arena);
|
||||||
|
TEST_ASSERT_NOT_NULL(objects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_arena_get_put_limit(void) {
|
||||||
|
struct test_5 *object = NULL;
|
||||||
|
struct test_5 *objects[NUM_OBJECTS] = {};
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_OBJECTS; i++) {
|
||||||
|
objects[i] = ucl_arena_get(arena);
|
||||||
|
TEST_ASSERT_NOT_NULL(objects[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ucl_arena_put(arena, (void *) objects[4]);
|
||||||
|
struct test_5 *spare = ucl_arena_get(arena);
|
||||||
|
TEST_ASSERT_EQUAL(spare, objects[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_arena_get_over_limit(void) {
|
||||||
|
struct test_5 *object = NULL;
|
||||||
|
struct test_5 *objects[NUM_OBJECTS + 1] = {};
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_OBJECTS + 1; i++) {
|
||||||
|
objects[i] = ucl_arena_get(arena);
|
||||||
|
if (i < NUM_OBJECTS) {
|
||||||
|
TEST_ASSERT_NOT_NULL(objects[i]);
|
||||||
|
} else {
|
||||||
|
TEST_ASSERT_NULL(objects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_arena_single);
|
||||||
|
RUN_TEST(test_arena_get_all);
|
||||||
|
RUN_TEST(test_arena_get_put_limit);
|
||||||
|
RUN_TEST(test_arena_get_over_limit);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user