diff --git a/src/include/util/hb_arena.h b/src/include/util/hb_arena.h index 907aede22..8c89be6d5 100644 --- a/src/include/util/hb_arena.h +++ b/src/include/util/hb_arena.h @@ -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 diff --git a/src/util/hb_arena.c b/src/util/hb_arena.c index a17b67005..44be945ff 100644 --- a/src/util/hb_arena.c +++ b/src/util/hb_arena.c @@ -9,11 +9,35 @@ #include #include #include + +#ifdef HB_USE_MALLOC +#include +#else #include +#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)); @@ -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); @@ -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; @@ -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) { @@ -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) { @@ -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; diff --git a/test/c/test_hb_arena.c b/test/c/test_hb_arena.c index b3266a2d3..c338b5af3 100644 --- a/test/c/test_hb_arena.c +++ b/test/c/test_hb_arena.c @@ -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 @@ -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);