Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/include/util/hb_arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void* hb_arena_alloc(hb_arena_T* allocator, size_t size);
size_t hb_arena_position(hb_arena_T* allocator);
size_t hb_arena_capacity(hb_arena_T* allocator);
void hb_arena_reset(hb_arena_T* allocator);
void hb_arena_reset_to(hb_arena_T* allocator, size_t new_position);
void hb_arena_reset_to(hb_arena_T* allocator, size_t target_position);
void hb_arena_free(hb_arena_T* allocator);

#endif
130 changes: 72 additions & 58 deletions src/util/hb_arena.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,35 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#ifdef HB_USE_MALLOC
#include <stdlib.h>
#else
#include <sys/mman.h>
#endif

#define hb_arena_for_each_page(allocator, page) \
for (hb_arena_page_T* page = (allocator)->head; page != NULL; page = page->next)

static void* hb_system_allocate_memory(size_t size) {
#ifdef HB_USE_MALLOC
return malloc(size);
#else
void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory == MAP_FAILED) { return NULL; }

return memory;
#endif
}

static void hb_system_free_memory(void* ptr, size_t size) {
#ifdef HB_USE_MALLOC
free(ptr);
#else
munmap(ptr, size);
#endif
}

static inline size_t hb_arena_align_size(size_t size, size_t alignment) {
assert(size <= SIZE_MAX - (alignment - 1));

Expand All @@ -26,7 +50,7 @@ static inline bool hb_arena_page_has_capacity(hb_arena_page_T* page, size_t requ
return page->position + required_size <= page->capacity;
}

static inline void* hb_arena_page_alloc_from(hb_arena_page_T* page, size_t size) {
static inline void* hb_arena_page_alloc(hb_arena_page_T* page, size_t size) {
assert(size > 0);
assert(page->position + size <= page->capacity);

Expand All @@ -36,29 +60,18 @@ static inline void* hb_arena_page_alloc_from(hb_arena_page_T* page, size_t size)
return result;
}

static inline void hb_arena_page_reset(hb_arena_page_T* page) {
page->position = 0;
}

static inline void hb_arena_reset_pages_after(hb_arena_page_T* start_page) {
for (hb_arena_page_T* page = start_page; page != NULL; page = page->next) {
hb_arena_page_reset(page);
}
}

static bool hb_arena_append_page(hb_arena_T* allocator, size_t minimum_size) {
assert(minimum_size > 0);

size_t page_size = MAX(allocator->default_page_size, minimum_size);
static size_t hb_arena_page_free(hb_arena_page_T* starting_page);

static bool hb_arena_append_page(hb_arena_T* allocator, size_t page_size) {
assert(page_size <= SIZE_MAX - sizeof(hb_arena_page_T));
size_t total_size = page_size + sizeof(hb_arena_page_T);

hb_arena_page_T* page = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
size_t page_size_with_meta_data = page_size + sizeof(hb_arena_page_T);

if (page == MAP_FAILED) { return false; }
hb_arena_page_T* page = hb_system_allocate_memory(page_size_with_meta_data);
if (page == NULL) { return false; }

*page = (hb_arena_page_T) { .next = NULL, .capacity = page_size, .position = 0 };
page->next = NULL;
page->capacity = page_size;
page->position = 0;

if (allocator->head == NULL) {
allocator->head = page;
Expand All @@ -77,15 +90,15 @@ static bool hb_arena_append_page(hb_arena_T* allocator, size_t minimum_size) {
return true;
}

bool hb_arena_init(hb_arena_T* allocator, size_t initial_size) {
assert(initial_size > 0);
bool hb_arena_init(hb_arena_T* allocator, size_t default_page_size) {
assert(default_page_size > 0);

allocator->head = NULL;
allocator->tail = NULL;
allocator->default_page_size = initial_size;
allocator->default_page_size = default_page_size;
allocator->allocation_count = 0;

return hb_arena_append_page(allocator, initial_size);
return hb_arena_append_page(allocator, default_page_size);
}

void* hb_arena_alloc(hb_arena_T* allocator, size_t size) {
Expand All @@ -97,21 +110,17 @@ void* hb_arena_alloc(hb_arena_T* allocator, size_t size) {
allocator->allocation_count++;

if (hb_arena_page_has_capacity(allocator->tail, required_size)) {
return hb_arena_page_alloc_from(allocator->tail, required_size);
return hb_arena_page_alloc(allocator->tail, required_size);
}

for (hb_arena_page_T* page = allocator->tail->next; page != NULL; page = page->next) {
if (hb_arena_page_has_capacity(page, required_size)) {
allocator->tail = page;
return hb_arena_page_alloc_from(allocator->tail, required_size);
return hb_arena_page_alloc(allocator->tail, required_size);
}
}

bool allocated = hb_arena_append_page(allocator, required_size);

if (!allocated) { return NULL; }

return hb_arena_page_alloc_from(allocator->tail, required_size);
return hb_arena_page_alloc(allocator->tail, required_size);
}

size_t hb_arena_position(hb_arena_T* allocator) {
Expand All @@ -135,49 +144,54 @@ size_t hb_arena_capacity(hb_arena_T* allocator) {
}

void hb_arena_reset(hb_arena_T* allocator) {
hb_arena_for_each_page(allocator, page) {
hb_arena_page_reset(page);
}

allocator->tail = allocator->head;
allocator->allocation_count = 0;
hb_arena_reset_to(allocator, 0);
}

void hb_arena_reset_to(hb_arena_T* allocator, size_t target_position) {
if (target_position == 0) {
hb_arena_reset(allocator);
hb_arena_page_T* current_page = allocator->head;
size_t current_position = 0;

return;
while (current_page != NULL) {
current_position += current_page->position;

if (current_position >= target_position) {
current_page->position -= current_position - target_position;
break;
}

current_page = current_page->next;
}

size_t accumulated = 0;
if (current_page->next != NULL) {
size_t freed_size = hb_arena_page_free(current_page->next);
allocator->tail = current_page;
current_page->next = NULL;

hb_arena_for_each_page(allocator, page) {
if (accumulated + page->capacity >= target_position) {
page->position = target_position - accumulated;
allocator->tail = page;
hb_arena_append_page(allocator, freed_size);
}
}

hb_arena_reset_pages_after(page->next);
static size_t hb_arena_page_free(hb_arena_page_T* starting_page) {
size_t freed_capacity = 0;

return;
}
for (hb_arena_page_T* current_page = starting_page; current_page != NULL;) {
hb_arena_page_T* next_page = current_page->next;

freed_capacity += current_page->capacity;

size_t total_size = sizeof(hb_arena_page_T) + current_page->capacity;
hb_system_free_memory(current_page, total_size);

accumulated += page->capacity;
page->position = page->capacity;
current_page = next_page;
}

return freed_capacity;
}

void hb_arena_free(hb_arena_T* allocator) {
if (allocator->head == NULL) { return; }

for (hb_arena_page_T* current = allocator->head; current != NULL;) {
hb_arena_page_T* next = current->next;
size_t total_size = sizeof(hb_arena_page_T) + current->capacity;

munmap(current, total_size);

current = next;
}
hb_arena_page_free(allocator->head);

allocator->head = NULL;
allocator->tail = NULL;
Expand Down
8 changes: 6 additions & 2 deletions test/c/test_hb_arena.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,13 @@ TEST(test_arena_reset_to_multipage)
size_t checkpoint = hb_arena_position(&allocator);

hb_arena_alloc(&allocator, 64);
hb_arena_alloc(&allocator, 128);
ck_assert_ptr_nonnull(allocator.head->next);

hb_arena_reset_to(&allocator, checkpoint);
ck_assert_int_eq(hb_arena_position(&allocator), checkpoint);
ck_assert_ptr_eq(allocator.tail, allocator.head);
ck_assert_ptr_eq(allocator.tail, allocator.head->next);
ck_assert_int_eq(allocator.head->next->capacity, 192);

hb_arena_free(&allocator);
END
Expand Down Expand Up @@ -308,7 +310,9 @@ TEST(test_arena_page_reuse_after_reset)

hb_arena_reset_to(&allocator, checkpoint);
ck_assert_int_eq(hb_arena_position(&allocator), checkpoint);
ck_assert_ptr_eq(allocator.tail, allocator.head);
ck_assert_ptr_eq(allocator.tail, allocator.head->next);
ck_assert_int_eq(allocator.head->next->capacity, 128);


char *memory = hb_arena_alloc(&allocator, 64);
ck_assert_ptr_nonnull(memory);
Expand Down
Loading