141 lines
4.5 KiB
C++
141 lines
4.5 KiB
C++
#pragma once
|
|
|
|
struct ExpandableArena; // fwd declare #temp
|
|
|
|
#if OS_WINDOWS
|
|
constexpr u32 ARENA_DEFAULT_COMMIT_PAGE_COUNT = 16; // 16 * 4k page = 64kB
|
|
constexpr s64 ARENA_DEFAULT_COMMIT_SIZE_BYTES = 65536;
|
|
#endif
|
|
|
|
constexpr u16 ARENA_DEFAULT_ALIGNMENT = CPU_REGISTER_WIDTH_BYTES;
|
|
|
|
#define ARENA_DEBUG BUILD_DEBUG
|
|
|
|
constexpr s32 Arena_Reserve_Count = 7;
|
|
enum class Arena_Reserve: u8 {
|
|
Size_64K = 0, // these are used as indices in Arena_Free_List!
|
|
Size_2M = 1,
|
|
Size_64M = 2,
|
|
Size_2G = 3,
|
|
Size_64G = 4,
|
|
Size_2T = 5,
|
|
Size_64T = 6,
|
|
};
|
|
|
|
constexpr s64 Arena_Sizes[Arena_Reserve_Count] = {
|
|
64LL * 1024,
|
|
2LL * 1024 * 1024,
|
|
64LL * 1024 * 1024,
|
|
2LL * 1024 * 1024 * 1024,
|
|
64LL * 1024 * 1024 * 1024,
|
|
2LL * 1024 * 1024 * 1024 * 1024,
|
|
64LL * 1024 * 1024 * 1024 * 1024,
|
|
};
|
|
|
|
enum class Arena_Flags: u8 {
|
|
None = 0,
|
|
Chained = 0x01,
|
|
Is_Bootstrapped = 0x02,
|
|
Large_Pages = 0x40,
|
|
// Secure_Arena = 0xF0 // #NOTE: Secure Arenas are not implemented yet!
|
|
};
|
|
|
|
force_inline Arena_Flags operator | (Arena_Flags a, Arena_Flags b) {
|
|
return (Arena_Flags)(((u8)a) | ((u8)b));
|
|
}
|
|
|
|
force_inline Arena_Flags operator & (Arena_Flags a, Arena_Flags b) {
|
|
return (Arena_Flags)(((u8)a) & ((u8)b));
|
|
}
|
|
|
|
force_inline Arena_Flags& operator |= (Arena_Flags& a, Arena_Flags b) {
|
|
a = a | b;
|
|
return a;
|
|
}
|
|
|
|
force_inline Arena_Flags operator ~ (Arena_Flags a) {
|
|
return (Arena_Flags)(~((u8)a));
|
|
}
|
|
|
|
struct Arena {
|
|
u8* current_point = nullptr;
|
|
u8* memory_base = nullptr;
|
|
u8* first_uncommitted_page = nullptr;
|
|
u16 alignment = CPU_REGISTER_WIDTH_BYTES;
|
|
Arena_Reserve reserve_size = Arena_Reserve::Size_64K;
|
|
Arena_Flags flags = Arena_Flags::None;
|
|
u32 initial_commit_page_count = ARENA_DEFAULT_COMMIT_PAGE_COUNT;
|
|
};
|
|
|
|
typedef void* (*Memory_Wipe_Function)(void* memory, u64 byte_count);
|
|
|
|
void* arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
|
|
|
|
// Interface API for normal use (idk how to explain - see Arena_Free_List.cpp)
|
|
|
|
void initialize_arena_free_list (Allocator allocator);
|
|
Arena* next_arena (Arena_Reserve reserve_size);
|
|
void release_arena (Arena* arena, bool delete_extra_pages=true);
|
|
|
|
// Main API
|
|
Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count);
|
|
|
|
void arena_init (Arena* arena, Arena_Reserve new_reserve, s32 default_commit_page_count=16); // For when we're *not* bootstrapping arenas: (I'm debating if we should keep this..)
|
|
bool arena_commit_first_pages (Arena* arena, s64 commit_size, s64 start_offset=0); // This is useful for initializing arenas (arena_init), and for starting Arena-backed arrays.
|
|
|
|
void arena_clear_flags (Arena* arena);
|
|
void arena_set_bootstrap_flag (Arena* arena);
|
|
void arena_set_secure_flag (Arena* arena);
|
|
|
|
bool arena_is_bootstrapped (Arena* arena);
|
|
|
|
void arena_reset_keeping_memory (Arena* arena); // just sets current point to arena_start
|
|
void arena_reset (Arena* arena); // frees excess pages
|
|
void arena_reset_overwriting_memory (Arena* arena, Memory_Wipe_Function wipe_function);
|
|
|
|
// Internal API (should not be called directly by program)
|
|
void* arena_alloc (Arena* arena, s64 byte_count);
|
|
|
|
// Utilties
|
|
u8* arena_start (Arena* arena);
|
|
u8* arena_address_limit (Arena* arena);
|
|
s64 arena_usage_bytes (Arena* arena);
|
|
s64 arena_usage_committed_bytes (Arena* arena);
|
|
// arena_details (requires print)
|
|
s64 reserve_size (Arena* arena);
|
|
s64 reserve_size (Arena_Reserve ar);
|
|
bool is_valid (Arena* arena);
|
|
Allocator allocator (Arena* arena);
|
|
|
|
// Platform-Specific Implementations (forward-declared)
|
|
void platform_init (Arena* arena, s64 new_reserve);
|
|
void extend_committed_pages (Arena* arena, u8* end);
|
|
void free_pages_down_to (Arena* arena, s64 pages_to_keep);
|
|
void arena_delete (Arena* arena);
|
|
|
|
Arena_Reserve next_reserve_size (s64 size);
|
|
|
|
struct Push_Alignment { // #rename to Arena_Push_Alignment?
|
|
Arena* arena;
|
|
u16 original_alignment;
|
|
|
|
Push_Alignment(Arena* arena, u16 alignment) {
|
|
Assert(is_valid(arena));
|
|
this->arena = arena;
|
|
this->original_alignment = arena->alignment;
|
|
this->arena->alignment = alignment;
|
|
}
|
|
|
|
Push_Alignment(ExpandableArena* arena_ex, u16 alignment) {
|
|
Push_Alignment((Arena*)arena_ex, alignment);
|
|
}
|
|
|
|
~Push_Alignment() {
|
|
arena->alignment = original_alignment;
|
|
}
|
|
};
|
|
|
|
|
|
// Do this later:
|
|
// arena_lock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|
|
// arena_unlock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|