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

252 lines
7.6 KiB
C++

#include "Arena.h"
#include "Arena_Windows.cpp"
// For arrays, use `Array`, which is backed by the general purpose allocator
// or use `ArenaArray` if you need to expand the size to an unknown size.
void* arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
Arena* arena = (Arena*)allocator_data;
Assert(arena != nullptr);
switch (mode) {
case Allocator_Mode::ALLOCATE: {
return arena_alloc(arena, requested_size);
} break;
case Allocator_Mode::RESIZE: {
Assert(false); // DO NOT USE RESIZE WITH ARENAS! :ArenaResizing
// Or maybeeee...
// Resize should check if current_point matches the end of the old allocation?
// and resize accordingly + pass back same pointer.
void* result = arena_alloc(arena, requested_size);
s64 size_to_copy = old_size < requested_size ? old_size : requested_size;
if (result && size_to_copy) {
memcpy(result, old_memory, size_to_copy);
}
return result;
} break;
case Allocator_Mode::DEALLOCATE:
return nullptr; // unused
break;
}
return nullptr;
}
// See arena_delete for decommissioning of arenas initialized with arena_init
void arena_init (Arena* arena, Arena_Reserve new_reserve, s32 default_commit_page_count) {
Assert(arena != nullptr);
s64 commit_size = default_commit_page_count * PLATFORM_MEMORY_PAGE_SIZE;
Assert(commit_size >= 0);
if (!is_valid(arena)) {
arena->reserve_size = new_reserve;
platform_init(arena, reserve_size(arena));
arena->current_point = arena_start(arena);
arena->first_uncommitted_page = arena->memory_base;
if (arena_commit_first_pages(arena, commit_size)) {
arena->initial_commit_page_count = default_commit_page_count;
// arena_lock_pages(arena, arena->memory_base, commit_size);
}
arena->alignment = ARENA_DEFAULT_ALIGNMENT;
arena->flags = (arena->flags | (~Arena_Flags::Is_Bootstrapped)); // unset is bootstrapped, if present.
}
}
bool arena_commit_first_pages (Arena* arena, s64 commit_size, s64 start_offset) {
Assert(commit_size > 0); // no point calling this without commit size
Assert(start_offset >= 0); // should NOT be negative.
if (commit_size <= 0 || start_offset < 0) {
return false;
}
u8* final_address = Align<u8*>(arena->memory_base + commit_size + start_offset,
arena->alignment);
s64 commit_size_final = (s64)(final_address - arena->memory_base);
if (commit_size_final > reserve_size(arena)) {
Assert(false); // Commit size exceeds reserve size
return false;
}
if (final_address > arena->first_uncommitted_page) {
extend_committed_pages(arena, final_address);
}
return true;
}
Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count) {
s64 commit_size = default_commit_page_count * PLATFORM_MEMORY_PAGE_SIZE;
Assert(commit_size <= reserve_size(new_reserve));
Arena new_arena;
arena_init(&new_arena, new_reserve, default_commit_page_count);
Arena* arena_ptr = (Arena*)new_arena.memory_base;
if (arena_ptr == nullptr) { Assert(false); // Assert in debug mode!
return nullptr;
}
memcpy(arena_ptr, &new_arena, sizeof(Arena));
arena_ptr->current_point = arena_start(arena_ptr);
arena_set_bootstrap_flag(arena_ptr);
return arena_ptr;
}
void arena_clear_flags (Arena* arena) { arena->flags = Arena_Flags::None; }
void arena_set_bootstrap_flag (Arena* arena) { arena->flags |= Arena_Flags::Is_Bootstrapped; }
bool arena_is_bootstrapped (Arena* arena) { return (bool)(arena->flags & Arena_Flags::Is_Bootstrapped); }
// void arena_set_secure_flag (Arena* arena) { arena->flags |= Arena_Flags::Secure_Arena; }
void arena_reset_keeping_memory (Arena* arena) {
if (!is_valid(arena)) return;
arena->current_point = arena_start(arena);
}
void arena_reset (Arena* arena) {
if (!is_valid(arena)) return;
arena->current_point = arena_start(arena);
free_pages_down_to(arena, arena->initial_commit_page_count);
}
void arena_reset_overwriting_memory (Arena* arena, Memory_Wipe_Function wipe_function) {
if (!is_valid(arena)) return;
Assert(wipe_function != nullptr);
if (wipe_function == nullptr) return;
wipe_function(arena_start(arena), (u64)(arena->current_point - arena_start(arena)));
arena_reset(arena);
}
void* arena_alloc (Arena* arena, s64 byte_count) {
Assert(arena != nullptr);
if (!arena->memory_base) { arena_init(arena, arena->reserve_size); }
Assert(is_valid(arena));
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)) {
printf("[Error] Failed to allocate because Arena is full and cannot expand!\n");
Assert(false); // Failed to allocate because arena is full and cannot expand!
} else {
extend_committed_pages(arena, result_end);
}
}
arena->current_point = result_end;
return result;
}
u8* arena_start (Arena* arena) {
// This assumes every arena is bootstrapped, so there may be some
// wastage/cache misses caused by this!
return Align(arena->memory_base + sizeof(Arena), ARENA_DEFAULT_ALIGNMENT);
}
u8* arena_address_limit (Arena* arena) {
return (arena->memory_base + reserve_size(arena));
}
bool is_valid (Arena* arena) {
return (arena != nullptr) && (arena->memory_base != nullptr);
}
s64 reserve_size (Arena* arena) {
return reserve_size(arena->reserve_size);
}
s64 reserve_size (Arena_Reserve ar) {
switch (ar) {
case Arena_Reserve::Size_64K: return KB(64);
case Arena_Reserve::Size_2M: return MB(2);
case Arena_Reserve::Size_64M: return MB(64);
case Arena_Reserve::Size_2G: return GB(2);
case Arena_Reserve::Size_64G: return GB(64);
case Arena_Reserve::Size_2T: return TB(2);
}
Assert(false); // This should not happen.
return 0;
}
Arena_Reserve next_reserve_size (s64 size) {
for (u8 i = 0; i < Arena_Reserve_Count; i += 1) {
if (size <= Arena_Sizes[i]) {
return (Arena_Reserve)i;
}
}
return Arena_Reserve::Size_64T;
}
// arena_usage_bytes is kinda pointless tbh.
s64 arena_usage_bytes (Arena* arena) { return (s64)(arena->current_point - arena_start(arena)); }
s64 arena_usage_committed_bytes (Arena* arena) { return (s64)(arena->first_uncommitted_page - arena->memory_base); }
// for arena details, I need to setup my string builder first.
Allocator allocator (Arena* arena) {
return { arena_allocator_proc, arena };
}
struct Auto_Reset {
Arena* arena;
u8* starting_point;
Auto_Reset(Arena* arena) {
Assert(is_valid(arena));
this->arena = arena;
this->starting_point = arena->current_point;
}
// #TODO: Implement with ExpandableArena
// Auto_Reset(ExpandableArena* arena_ex) {
// Auto_Reset((Arena*)arena_ex);
// }
~Auto_Reset() {
arena->current_point = starting_point;
}
};
struct Auto_Release {
bool is_expandable;
ExpandableArena* arena_ex;
Arena* arena;
u8* starting_point;
Auto_Release(ExpandableArena* arena_ex) {
this->arena_ex = arena_ex;
this->arena = arena_ex->current;
this->starting_point = arena_ex->current->current_point;
this->is_expandable = true;
}
Auto_Release(Arena* arena) {
this->arena_ex = nullptr;
this->arena = arena;
this->starting_point = arena->current_point;
this->is_expandable = false;
Assert(is_valid(arena));
}
~Auto_Release() {
if (is_expandable) {
arena_reset_to(arena_ex, arena, starting_point);
} else {
arena->current_point = starting_point;
free_pages_down_to(arena, arena->initial_commit_page_count);
}
}
};