base_math, only has Vec2 and Vec3; memory arena implementation with support for windows right now.

This commit is contained in:
Tijani Lawal 2024-08-31 13:39:34 -05:00
parent 9e1e4c2c5c
commit 8b7af0c1ec
10 changed files with 320 additions and 0 deletions

127
src/base/base_arena.c Normal file
View File

@ -0,0 +1,127 @@
internal Arena *arena_alloc_(ArenaParams *params) {
u64 reserve_size = params->reserve_size;
u64 commit_size = params->commit_size;
if (params->flags & ArenaFlag_LargePages) {
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
} else {
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
}
// reserve/commit initial block of memory
void *base = params->optional_backing_buffer;
if (base == 0) {
if (params->flags & ArenaFlag_LargePages) {
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
} else {
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
Arena *arena = (Arena *)base;
arena->current = arena; // Current arena in the chain
arena->flags = params->flags;
arena->commit_size = (u32)params->commit_size;
arena->reserve_size = params->reserve_size;
arena->base_position = 0;
arena->position = ARENA_HEADER_SIZE;
arena->commit = commit_size;
arena->reserve = reserve_size;
return arena;
}
internal void arena_release(Arena *arena) {
for (Arena *current_arena = arena->current, *previous_arena = 0; current_arena != 0; current_arena = previous_arena) {
previous_arena = current_arena->previous;
os_release(current_arena, current_arena->reserve);
}
}
internal void *arena_push(Arena *arena, u64 size_to_push, u64 align) {
Arena *current_arena = arena->current;
u64 position_pre_push = AlignPow2(current_arena->position, align); // position of arena before push
u64 position_post_push = position_pre_push + size_to_push; // position of arena after push
// ??(tijani): chain, if needed
if (current_arena->reserve < position_post_push && !(arena->flags & ArenaFlag_NoChain)) {
u64 new_reserve_size = current_arena->reserve_size;
u64 new_commit_size = current_arena->commit_size;
if (size_to_push > new_reserve_size) {
new_reserve_size = size_to_push + ARENA_HEADER_SIZE;
new_commit_size = size_to_push + ARENA_HEADER_SIZE;
}
Arena *new_memory_block =
arena_alloc(.reserve_size = new_reserve_size, .commit_size = new_commit_size, .flags = current_arena->flags);
new_memory_block->base_position = current_arena->base_position + current_arena->reserve;
SLLStackPush_N(arena->current, new_memory_block, previous); // `previous` refers to `previous` in `Arena` struct
current_arena = new_memory_block;
position_pre_push = AlignPow2(current_arena->position, align);
position_post_push = position_pre_push + size_to_push;
}
// ??(tijani): commit new page, if needed
if (current_arena->commit < position_post_push && !(current_arena->flags & ArenaFlag_LargePages)) {
u64 commit_post_aligned = AlignPow2(position_post_push, current_arena->commit_size);
u64 commit_post_clamped = ClampTop(commit_post_aligned, current_arena->reserve);
u64 new_commit_size = commit_post_clamped - current_arena->commit;
os_commit((u8 *)current_arena + current_arena->commit, new_commit_size);
}
// push onto the current block
void *memory = 0;
if (current_arena->commit >= position_post_push) {
memory = (u8 *)current_arena + position_pre_push;
current_arena->position = position_post_push;
// ??(tijani): why need to Asan Unpoision memory
}
return memory;
}
internal u64 arena_position(Arena *arena) {
Arena *current_arena = arena->current;
u64 current_position = current_arena->base_position + current_arena->position;
return current_position;
}
internal void arena_pop_to(Arena *arena, u64 position) {
u64 big_position = ClampBot(ARENA_HEADER_SIZE, position);
Arena *current_arena = arena->current;
for (Arena *previous_arena = 0; current_arena->base_position >= big_position; current_arena = previous_arena) {
previous_arena = current_arena->previous;
os_release(current_arena, current_arena->reserve);
}
arena->current = current_arena;
u64 new_position = big_position - current_arena->base_position;
// ??(tijani): why need to ASSERT and AsanPoision memory region.
current_arena->position = new_position;
}
internal void arena_clear(Arena *arena) { arena_pop_to(arena, 0); }
// Pops the amount of memory passed off the passed in arena.
// The new position of the passed in arena will be the same
// memory block minus the amount passed in.
internal void arena_pop_off(Arena *arena, u64 amount) {
u64 old_position = arena_position(arena);
u64 new_position = old_position;
if (amount < old_position) {
new_position = old_position - amount;
}
arena_pop_to(arena, new_position);
}
// Temporary memory
internal Temp temp_begin(Arena *arena) {
u64 position = arena_position(arena);
Temp temp = {arena, position};
return temp;
}
internal void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.position); }

78
src/base/base_arena.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef BASE_ARENA_H
#define BASE_ARENA_H
// Arena allocator simulates how stack memory works.
// This allows us the ability to allocate a reasonableamount of memory
// at startup and only use that allocated amount of memory throughout
// the lifetime of the application. Obviously this still uses
// "malloc, free, VirtualAlloc, VirtualFree" but instead of having to
// request and freeing memory during runtime, we create an environment
// where we have as much memory we could ever need and them portion it
// out in small chunk as needed. This has multiple benefits that would
// make itself glaring throughout the codebase but the gist of it is that
// we have one large block of memory throughout the lifetime,
// then application specific code that could need memory grab a chunck
// memory from the big block of memory we have reserved with the operating
// system at startup do whatever operation it needs to do, then return
// that block of memory back to the arena.
// Constants
#define ARENA_HEADER_SIZE 64
// Flags
// ArenaFlag_NoChain - Arenas are chained by default, this flag tells it otherwise.
// ArenaFlag_LargePages - Use large pages.
typedef u32 ArenaFlags;
enum {
ArenaFlag_NoChain = (1 << 0),
ArenaFlag_LargePages = (1 << 1),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams {
ArenaFlags flags;
u64 reserve_size;
u64 commit_size;
void *optional_backing_buffer; // NOTE(tijani): ??
};
typedef struct Arena Arena;
struct Arena {
Arena *previous; // Previous arena in the chain
Arena *current; // Current arena in the chain
ArenaFlags flags;
u32 commit_size;
u64 reserve_size;
u64 base_position;
u64 position;
u64 commit;
u64 reserve;
};
typedef struct Temp Temp;
struct Temp {
Arena *arena;
u64 position;
};
// creation, destruction
internal Arena *arena_alloc_(ArenaParams *params);
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__})
internal void arena_release(Arena *arena);
// push, pop operations
internal void *arena_push(Arena *arena, u64 size_to_push, u64 align);
internal u64 arena_position(Arena *arena);
internal void arena_pop_to(Arena *arena, u64 position);
internal void arena_clear(Arena *arena);
internal void arena_pop_off(Arena *arena, u64 amount);
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T) * (c), (align))
#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T) * (c))
#define push_array_no_zero(arena, Type, count) push_array_no_zero_aligned(arena, Type, count, Max(8, Align_Of(Type)))
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, Align_Of(T)))
// Temp
internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
#endif // BASE_ARENA_H

4
src/base/base_inc.c Normal file
View File

@ -0,0 +1,4 @@
// clang-format off
// clang-format on

11
src/base/base_inc.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef BASE_INC_H
#define BASE_INC_H
// clang-format off
#include "base_context_switching.h"
#include "base_core.h"
#include "base_math.h"
// clang-format on
#endif // BASE_INC_H

23
src/base/base_math.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef BASE_MATH_H
#define BASE_MATH_H
typedef struct Vec2F32 Vec2F32;
union Vec2F32 {
struct {
f32 x;
f32 y;
};
f32 v[2];
};
typedef struct Vec3F32 Vec3F32;
union Vec3F32 {
struct {
f32 x;
f32 y;
f32 z;
};
f32 v[2];
};
#endif // BASE_MATH_H

16
src/os/core/os_core.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef OS_CORE_H
#define OS_CORE_H
// %os_hooks(implemented per-os) Memory allocation
// NOTE(tijani): For normal page size
internal b32 os_commit(void *ptr, u64 size);
internal void *os_reserve(u64 size);
internal void os_decommit(void *ptr, u64 size);
internal void os_release(void *ptr, u64 size);
// NOTE(tijani): For large page size
internal void *os_reserve_large(u64 size);
internal b32 os_commit_large(void *ptr, u64 size);
#endif // OS_CORE_H

View File

@ -0,0 +1,33 @@
// %os_hooks(implemented per-os) Memory allocation
// NOTE(tijani): For normal page size
internal b32 os_commit(void *ptr, u64 size) {
b32 l_memory = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0);
return l_memory;
}
internal void *os_reserve(u64 size) {
void *l_memory = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return l_memory;
}
internal void os_decommit(void *ptr, u64 size) { VirtualFree(ptr, size, MEM_DECOMMIT); }
internal void os_release(void *ptr, u64 size) {
// NOTE(tijani): size must be 0 on windows hence it will fail.
VirtualFree(ptr, size, MEM_RELEASE);
}
// NOTE(tijani): For large page size
internal void *os_reserve_large(u64 size) {
void *l_memory = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
return l_memory;
}
// NOTE(tijani): From my understanding, on windows you cannot
// commit large pages as this has to be done during reservation.
// see `os_reserve_large` and WIN32 docs on large page support.
internal b32 os_commit_large(void *ptr, u64 size) { return -1; }
// %os_hooks(implemented per-os) Aborting
internal void os_abort(s32 exit_code) { ExitProcess(exit_code); }

View File

@ -0,0 +1,11 @@
#ifndef OS_CORE_WIN32_H
#define OS_CORE_WIN32_H
// clang-format off
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// clang-format on
#endif // OS_CORE_WIN32_H

6
src/os/os_inc.c Normal file
View File

@ -0,0 +1,6 @@
// clang-format off
#include "core/os_core.c"
#include "core/win32/os_core_win32.c"
// clang-format on

11
src/os/os_inc.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef OS_INC_H
#define OS_INC_H
// clang-format off
#include "core/os_core.h"
#include "core/win32/os_core_win32.h"
// clang-format on
#endif // OS_INC_H