From a87cab2452981c4f9e176fc053c4c747405d97fe Mon Sep 17 00:00:00 2001 From: Max Regan Date: Mon, 14 Nov 2022 22:33:23 -0500 Subject: [PATCH] Add arena allocator --- SConstruct | 2 + src/arena.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++ src/arena.h | 11 +++++ test/test_arena.c | 76 ++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 src/arena.c create mode 100644 src/arena.h create mode 100644 test/test_arena.c diff --git a/SConstruct b/SConstruct index b86d426..7b4514d 100644 --- a/SConstruct +++ b/SConstruct @@ -11,6 +11,7 @@ src_dir = "src/" program_sources = ["src/main.c"] lib_srcs = [ + "src/arena.c", "src/builtins.c", "src/evaluate.c", "src/memory.c", @@ -23,6 +24,7 @@ lib_includes = ["src/"] test_srcs = [ + "test/test_arena.c", "test/test_e2e.c", "test/test_parse.c", "test/test_state.c", diff --git a/src/arena.c b/src/arena.c new file mode 100644 index 0000000..66fd646 --- /dev/null +++ b/src/arena.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +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); +} diff --git a/src/arena.h b/src/arena.h new file mode 100644 index 0000000..1d73280 --- /dev/null +++ b/src/arena.h @@ -0,0 +1,11 @@ +#ifndef _UCLISP_ARENA_H_ +#define _UCLISP_ARENA_H_ + +#include + +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 diff --git a/test/test_arena.c b/test/test_arena.c new file mode 100644 index 0000000..a109baf --- /dev/null +++ b/test/test_arena.c @@ -0,0 +1,76 @@ +#include + +#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(); +}