214 lines
6.8 KiB
C++
214 lines
6.8 KiB
C++
constexpr size_t ARENA_BLOCK_SIZE = mib(4);
|
|
constexpr size_t ARENA_ALIGNMENT = 8;
|
|
|
|
struct Scratch_Arena : Allocator {
|
|
int cap, len;
|
|
uint8_t memory[];
|
|
};
|
|
|
|
struct Scratch_Scope {
|
|
Scratch_Arena *arena;
|
|
int pos;
|
|
Scratch_Scope(Scratch_Arena *arena) { this->arena = arena; this->pos = arena->len; }
|
|
~Scratch_Scope() { this->arena->len = this->pos; }
|
|
};
|
|
|
|
static void *scratch_arena_push_size(Scratch_Arena *arena, size_t size) {
|
|
size_t generous_size = size + ARENA_ALIGNMENT;
|
|
if (arena->len + generous_size > arena->cap) {
|
|
assert(size < arena->cap);
|
|
assert_message(0, "Scratch arena is too small, failed to handle a request, capacity: %d, occupied: %d, allocation: %d", generous_size);
|
|
return 0;
|
|
}
|
|
|
|
size_t aligned_fill = align_up((size_t)arena->memory + arena->len, ARENA_ALIGNMENT) - (size_t)arena->memory;
|
|
assert(((int64_t)aligned_fill - (int64_t)arena->len) >= 0);
|
|
assert(((int64_t)aligned_fill - (int64_t)arena->len) <= ARENA_ALIGNMENT);
|
|
arena->len = aligned_fill;
|
|
|
|
uint8_t *result = arena->memory + arena->len;
|
|
arena->len += size;
|
|
return (void *)result;
|
|
}
|
|
|
|
static Scratch_Arena *make_scratch_arena(void *buff, size_t size) {
|
|
Scratch_Arena *scratch = (Scratch_Arena *)buff;
|
|
scratch->len = 0;
|
|
scratch->cap = size - sizeof(Scratch_Arena);
|
|
scratch->allocate = (Allocator::Allocate *)scratch_arena_push_size;
|
|
scratch->deallocate = deallocate_stub;
|
|
return scratch;
|
|
}
|
|
|
|
static Scratch_Arena *allocate_scratch_arena(Allocator *allocator, size_t size_without_members) {
|
|
Scratch_Arena *scratch = (Scratch_Arena *)allocate_size(allocator, size_without_members + sizeof(Scratch_Arena), false);
|
|
scratch->cap = size_without_members;
|
|
scratch->len = 0;
|
|
scratch->allocate = (Allocator::Allocate *)scratch_arena_push_size;
|
|
scratch->deallocate = deallocate_stub;
|
|
return scratch;
|
|
}
|
|
|
|
struct Push_Arena_Block {
|
|
Push_Arena_Block *next;
|
|
uint8_t memory[];
|
|
};
|
|
|
|
struct Push_Arena : Allocator {
|
|
Allocator *block_allocator;
|
|
Push_Arena_Block *blocks;
|
|
size_t allocated_block_count;
|
|
size_t block_fill;
|
|
size_t wasted_memory;
|
|
};
|
|
|
|
static void push_arena_add_block(Push_Arena *arena) {
|
|
Push_Arena_Block *block = (Push_Arena_Block *)allocate_size(arena->block_allocator, sizeof(Push_Arena_Block) + ARENA_BLOCK_SIZE, false);
|
|
memory_zero(block, sizeof(Push_Arena_Block));
|
|
SLL_STACK_ADD(arena->blocks, block);
|
|
if (arena->allocated_block_count) arena->wasted_memory += ARENA_BLOCK_SIZE - arena->block_fill;
|
|
arena->allocated_block_count += 1;
|
|
arena->block_fill = 0;
|
|
}
|
|
|
|
static void push_arena_deallocate(Push_Arena *arena) {
|
|
for(Push_Arena_Block *it = arena->blocks; it; it = it->next) {
|
|
deallocate(arena->block_allocator, it);
|
|
}
|
|
memory_zero(arena, sizeof(*arena));
|
|
}
|
|
|
|
static void *push_arena_push_size(Push_Arena *arena, size_t size) {
|
|
assert(arena->block_allocator);
|
|
assert(size < ARENA_BLOCK_SIZE / 2);
|
|
size_t generous_size = size + ARENA_ALIGNMENT;
|
|
if (arena->blocks == 0 || (arena->block_fill + generous_size > ARENA_BLOCK_SIZE)) {
|
|
push_arena_add_block(arena);
|
|
}
|
|
|
|
Push_Arena_Block *L = arena->blocks;
|
|
size_t aligned_fill = align_up((size_t)L->memory + arena->block_fill, ARENA_ALIGNMENT) - (size_t)L->memory;
|
|
assert(((int64_t)aligned_fill - (int64_t)arena->block_fill) >= 0);
|
|
assert(((int64_t)aligned_fill - (int64_t)arena->block_fill) <= ARENA_ALIGNMENT);
|
|
arena->block_fill = aligned_fill;
|
|
|
|
uint8_t *result = L->memory + arena->block_fill;
|
|
arena->block_fill += size;
|
|
return (void *)result;
|
|
}
|
|
|
|
static Push_Arena make_push_arena(Allocator *allocator) {
|
|
Push_Arena result = {};
|
|
result.block_allocator = allocator;
|
|
result.allocate = (Allocator::Allocate *)push_arena_push_size;
|
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
|
return result;
|
|
}
|
|
|
|
struct CRT_Heap : Allocator {};
|
|
static void *crt_allocate(Allocator *allocator, size_t size) { return malloc(size); }
|
|
static void crt_deallocate(Allocator *allocator, void *p) { return free(p); }
|
|
static CRT_Heap make_crt_heap() {
|
|
CRT_Heap result = {};
|
|
result.allocate = crt_allocate;
|
|
result.deallocate = crt_deallocate;
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Memory OS
|
|
//-----------------------------------------------------------------------------
|
|
struct OS_Memory{
|
|
size_t commit, reserve;
|
|
U8 *data;
|
|
};
|
|
CORE_Static OS_Memory os_reserve(size_t size);
|
|
CORE_Static B32 os_commit(OS_Memory *m, size_t size);
|
|
CORE_Static void os_release(OS_Memory *m);
|
|
CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Memory arenas
|
|
//-----------------------------------------------------------------------------
|
|
global const size_t default_reserve_size = gib(4);
|
|
global const size_t default_alignment = 8;
|
|
global const size_t additional_commit_size = mib(1);
|
|
struct Arena : Allocator {
|
|
OS_Memory memory;
|
|
size_t alignment;
|
|
size_t len;
|
|
String debug_string;
|
|
};
|
|
|
|
CORE_Static void
|
|
arena_pop_pos(Arena *arena, size_t pos){
|
|
pos = clamp_top(pos, arena->len);
|
|
arena->len = pos;
|
|
}
|
|
|
|
CORE_Static void *
|
|
arena_pop(Arena *arena, size_t size){
|
|
size = clamp_top(size, arena->len);
|
|
arena->len -= size;
|
|
return arena->memory.data + arena->len;
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_release(Arena *arena){
|
|
os_release(&arena->memory);
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_clear(Arena *arena){
|
|
arena_pop_pos(arena, 0);
|
|
}
|
|
|
|
CORE_Static void *
|
|
arena_push_size(Arena *a, size_t size){
|
|
size_t generous_size = size + a->alignment;
|
|
if(a->len+generous_size>a->memory.commit){
|
|
assert(a->memory.reserve > 0);
|
|
B32 result = os_commit(&a->memory, generous_size+additional_commit_size);
|
|
assert(result);
|
|
}
|
|
|
|
a->len = align_up(a->len, a->alignment);
|
|
assert(a->memory.reserve > a->len + size);
|
|
void *result = (U8*)a->memory.data + a->len;
|
|
a->len += size;
|
|
|
|
return result;
|
|
}
|
|
|
|
CORE_Static Arena
|
|
push_arena(Allocator *allocator, size_t size, String debug_name) {
|
|
Arena result = {};
|
|
result.memory.data = (U8 *)allocate_size(allocator, size);
|
|
result.memory.reserve = size;
|
|
result.alignment = default_alignment;
|
|
result.debug_string = debug_name;
|
|
result.allocate = (Allocator::Allocate *)arena_push_size;
|
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_init(Arena *a, String debug_name){
|
|
a->memory = os_reserve(default_reserve_size);
|
|
a->alignment = default_alignment;
|
|
a->debug_string = debug_name;
|
|
a->allocate = (Allocator::Allocate *)arena_push_size;
|
|
a->deallocate = (Allocator::Deallocate *)deallocate_stub;
|
|
}
|
|
|
|
CORE_Static Arena
|
|
arena_sub(Arena *base, size_t size, String debug_name) {
|
|
Arena result = {};
|
|
result.memory.data = (U8 *)arena_push_size(base, size);
|
|
result.memory.commit = size;
|
|
result.memory.reserve = size;
|
|
result.alignment = default_alignment;
|
|
result.len = 0;
|
|
return result;
|
|
}
|