Musa-Cpp-Lib-V2/lib/Base/Expandable_Arena.cpp

139 lines
4.7 KiB
C++

ExpandableArena* expandable_arena_new (Arena_Reserve starting_reserve, s32 commit_page_count) {
ExpandableArena* new_arena = (ExpandableArena*)bootstrap_arena(starting_reserve, commit_page_count);
// Note: beyond first 32 bytes (sizeof(Arena)) ExpandableArena will not be initialized,
// so we do it here:
new_arena->current = (Arena*)new_arena;
new_arena->current_point = expandable_arena_start(new_arena);
new_arena->next_arenas = Array<Arena*>(); // next_arenas will be uninitialized, so we have to do this
// We have to use malloc because if we reset this new arena, all the data will be lost
// We don't want to tie the lifetime of next_arenas to this expandable arena.
new_arena->next_arenas.allocator = GPAllocator();
array_reserve(new_arena->next_arenas, 8);
return new_arena;
}
void* expandable_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
ExpandableArena* arena = (ExpandableArena*)allocator_data;
Assert(arena != nullptr);
switch (mode) {
case Allocator_Mode::ALLOCATE: {
return expandable_arena_alloc(arena, requested_size);
} break;
case Allocator_Mode::RESIZE: {
// See note :ArenaResizing
void* new_memory = expandable_arena_alloc(arena, requested_size);
memcpy(new_memory, old_memory, old_size);
return new_memory;
} break;
case Allocator_Mode::DEALLOCATE:
return nullptr;
break;
}
return nullptr;
}
bool is_valid (ExpandableArena* arena) {
return (arena != nullptr)
&& (arena->memory_base != nullptr)
&& (arena->current != nullptr);
}
void* expandable_arena_alloc (ExpandableArena* arena_ex, s64 byte_count) {
Assert(arena_ex != nullptr);
Assert(arena_ex->memory_base != nullptr); // must be initialized before calling.
Assert(is_valid(arena_ex));
Assert(arena_table->initialized);
Arena* arena = (Arena*)arena_ex->current;
u8* result = Align<u8*>(arena->current_point, arena->alignment);
u8* result_end = result + byte_count;
if (result_end > arena->first_uncommitted_page) {
if (result_end > arena_address_limit(arena)) {
// Pick an appropriate reserve size that will fit this allocation.
Arena_Reserve new_min_reserve = next_reserve_size(byte_count + sizeof(Arena));
if (arena->reserve_size > new_min_reserve) {
new_min_reserve = arena->reserve_size;
}
Arena* new_arena = next_arena(new_min_reserve);
new_arena->alignment = arena_ex->alignment;
new_arena->flags = arena_ex->flags;
arena_ex->current = new_arena;
array_add(arena_ex->next_arenas, new_arena);
// Allocate from new arena, committing pages as required
result = Align<u8*>(new_arena->current_point, new_arena->alignment);
result_end = result + byte_count;
if (result_end > new_arena->first_uncommitted_page) {
extend_committed_pages(new_arena, result_end);
}
} else {
extend_committed_pages(arena, result_end);
}
}
arena_ex->current->current_point = result_end;
return result;
}
u8* expandable_arena_start (ExpandableArena* arena_ex) {
return Align(arena_ex->memory_base + sizeof(ExpandableArena), ARENA_DEFAULT_ALIGNMENT);
}
Allocator allocator (ExpandableArena* arena_ex) {
return { expandable_arena_allocator_proc, arena_ex };
}
// #TODO: currently this keeps the final arena's memory. Fix this!
void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point) {
// going backwards from end of arena list
for (s64 i = arena_ex->next_arenas.count-1; i > 0; i -= 1) {
Arena* arena = arena_ex->next_arenas[i];
if (arena == last_arena) { // return to starting_point
arena_ex->current = arena;
arena_ex->current->current_point = starting_point;
break;
}
release_arena(arena);
array_unordered_remove_by_index(arena_ex->next_arenas, i);
}
}
void arena_reset (ExpandableArena* arena_ex, bool free_extra_pages) {
if (!is_valid(arena_ex)) return;
// Free arenas in `next_arenas`
for (s64 i = 0; i < arena_ex->next_arenas.count; i += 1) {
release_arena(arena_ex->next_arenas[i], free_extra_pages);
}
// Reset next_arenas
#if BUILD_DEBUG
array_zero(arena_ex->next_arenas);
#endif
array_reset_keeping_memory(arena_ex->next_arenas);
arena_ex->current = (Arena*)arena_ex;
arena_ex->current_point = expandable_arena_start(arena_ex);
if (free_extra_pages) {
free_pages_down_to((Arena*)arena_ex, arena_ex->initial_commit_page_count);
}
}
force_inline void arena_delete (ExpandableArena* arena_ex) {
array_free(arena_ex->next_arenas);
arena_reset(arena_ex, true);
arena_delete((Arena*)arena_ex);
}