From d8cb10b432c59b0e8786b67fa574cc1c866666b8 Mon Sep 17 00:00:00 2001 From: doyle Date: Mon, 7 Aug 2023 09:22:29 +1000 Subject: [PATCH] Initial commit --- .gitignore | 1 + build.bat | 19 +++++ rfleury_arena.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 .gitignore create mode 100644 build.bat create mode 100644 rfleury_arena.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b1c09d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Build diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..149350b --- /dev/null +++ b/build.bat @@ -0,0 +1,19 @@ +@echo off + +set script_dir_backslash=%~dp0 +set script_dir=%script_dir_backslash:~0,-1% +set build_dir=%script_dir%\Build +set code_dir=%script_dir% +if not exist %build_dir% mkdir %build_dir% + +set compile_flags=%code_dir%\rfleury_arena.cpp /W4 /Z7 /MD /EHsc /nologo +set link_flags=/link /incremental:no + +set msvc_cmd=cl %compile_flags% /Forfleury_arena /Ferfleury_arena %link_flags% +set clang_cmd=clang-cl %compile_flags% /Forfleury_arena /Ferfleury_arena %link_flags% + +pushd %build_dir% +powershell -Command "$duration = Measure-Command {%msvc_cmd% | Out-Default}; Write-Host 'msvc:' $duration.TotalSeconds 'seconds'" +powershell -Command "$duration = Measure-Command {%clang_cmd% | Out-Default}; Write-Host 'clang:' $duration.TotalSeconds 'seconds'" +popd +exit /B 1 diff --git a/rfleury_arena.cpp b/rfleury_arena.cpp new file mode 100644 index 0000000..06b1621 --- /dev/null +++ b/rfleury_arena.cpp @@ -0,0 +1,208 @@ +// NOTE: Includes ================================================================================== +#define NOMINMAX +#define WIN32_MEAN_AND_LEAN +#include + +#include +#include + +// NOTE: Types ===================================================================================== +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; + +#define MIN(a, b) (a) < (b) ? (a) : (b) +#define MAX(a, b) (a) > (b) ? (a) : (b) + +struct OS +{ + U16 page_size; + U16 alloc_granularity; +}; +OS g_os; + +struct ArenaBlock +{ + void *data; + U64 pos, commit, cap; + ArenaBlock *next, *prev; +}; + +struct Arena +{ + ArenaBlock *first, *last; + U32 flags; +}; + +enum ArenaFlag +{ + ArenaFlag_Chain = 1 << 0, + ArenaFlag_ReserveCommit = 1 << 1, + ArenaFlag_ChainReserveCommit = ArenaFlag_Chain | ArenaFlag_ReserveCommit, +}; + +#define KILOBYTES(val) 1024ULL * val +#define MEGABYTES(val) 1024ULL * KILOBYTES(val) +#define GIGABYTES(val) 1024ULL * MEGABYTES(val) +#define TERABYTES(val) 1024ULL * GIGABYTES(val) + +// NOTE: API ======================================================================================= +Arena *ArenaAlloc(U64 cap, uint32_t flags); +void ArenaRelease(Arena *arena); +U64 ArenaPos(Arena *arena); + +#define ArenaPushN(arena, T, size) (T *)ArenaPush(arena, sizeof(T) * size, alignof(T)) +void *ArenaPushNoZero(Arena *arena, U64 size, U64 alignment); +void *ArenaPush(Arena *arena, U64 size, U64 alignment); + +void ArenaPopTo(Arena *arena, U64 pos); +void ArenaPop(Arena *arena, U64 size); +void ArenaClear(Arena *arena); + +#define ArenaRealCommitPointer(block) (char *)block + block->commit + +#define ArenaRealCommitSize(block) arena->commit + sizeof(Arena) + sizeof(ArenaBlock) + +#define IsPowerOfTwo(value, pot) ((((uintptr_t)value) & (pot - 1)) == 0) +#define AlignUpPowerOfTwo(value, pot) (((uintptr_t)value) + ((pot) - (((uintptr_t)value) & ((pot) - 1)))) +#define AlignDownPowerOfTwo(value, pot) (((uintptr_t)value) & (~(pot - 1))) + +// NOTE: Implementation ============================================================================ +Arena *ArenaAlloc(U64 initial_cap, U32 flags) +{ + assert((flags & ~ArenaFlag_ChainReserveCommit) == 0); + + // NOTE: Allocate memory + U64 metadata_size = sizeof(Arena) + sizeof(ArenaBlock); + U64 alloc_size = AlignUpPowerOfTwo(metadata_size + initial_cap, g_os.page_size); + U32 os_flags = MEM_RESERVE | ((flags & ArenaFlag_ReserveCommit) ? 0 : MEM_COMMIT); + char *ptr = (char *)VirtualAlloc(nullptr, alloc_size, os_flags, PAGE_READWRITE); + char const *end = ptr + alloc_size; + + U64 real_commit_size = 0; + if ((os_flags & MEM_COMMIT)) { + real_commit_size = alloc_size; + } else { + real_commit_size = AlignUpPowerOfTwo(metadata_size, g_os.page_size); + VirtualAlloc(ptr, real_commit_size, MEM_COMMIT, PAGE_READWRITE); + } + + // NOTE: Create arena and block + Arena *result = (Arena *)ptr; + ptr += sizeof(*result); + ArenaBlock *block = (ArenaBlock *)ptr; + ptr += sizeof(*block); + block->data = ptr; + block->cap = end - ptr; + block->commit = real_commit_size - metadata_size; // NOTE: Raw commit size includes block metadata + assert(IsPowerOfTwo(block, alignof(ArenaBlock))); // NOTE: Enforce alignment of block + + // NOTE: Setup linked list of blocks + block->next = block->prev = block; + result->first = result->last = block->next; + return result; +} + +void ArenaRelease(Arena *arena) +{ + if (!arena) + return; + for (ArenaBlock *block = arena->first, *next = block->next; block->next != block->prev; block = next) + VirtualFree(block, 0, MEM_RELEASE); + VirtualFree(arena, 0, MEM_RELEASE); +} + +U64 ArenaPos(Arena *arena) +{ + U64 result = (arena && arena->last) ? arena->last->pos : 0; + return result; +} + +void *ArenaPushNoZero(Arena *arena, U64 size, U64 alignment) +{ + void *result = nullptr; + if (!arena) + return result; + + assert(IsPowerOfTwo(alignment, alignment)); // Enforce PoT + while ((arena->last->pos + size) > arena->last->cap) { + if ((arena->flags & ArenaFlag_Chain) == 0) + return result; + + U64 alloc_size = AlignUpPowerOfTwo(sizeof(ArenaBlock) + size, g_os.page_size); + ArenaBlock *block = (ArenaBlock *)VirtualAlloc(nullptr, alloc_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!block) + return result; + + block->cap = alloc_size - sizeof(*block); + block->data = (char *)block + sizeof(*block); + block->prev = arena->last; + block->next = arena->last->next; + block->prev->next = block; + block->next->prev = block; + block->commit = block->cap; + assert(((uintptr_t)block & alignof(ArenaBlock) - 1) == 0); // Enforce alignment of block + } + + // NOTE: Align PoT & divvy out the pointer + ArenaBlock *block = arena->last; + block->pos = AlignUpPowerOfTwo(block->pos, alignment) + size; + result = (char *)block->data + (block->pos - size); + + // NOTE: Commit pages + if (block->commit <= block->pos) { + assert(arena->flags & ArenaFlag_ReserveCommit); + void *commit_ptr = (char *)block->data + block->commit; + U64 commit_size = AlignUpPowerOfTwo(block->pos - block->commit, g_os.page_size); + assert(IsPowerOfTwo(commit_ptr, g_os.page_size)); + VirtualAlloc(commit_ptr, commit_size, MEM_COMMIT, PAGE_READWRITE); + block->commit += commit_size; + assert(IsPowerOfTwo(block->commit, g_os.page_size)); + } + + assert(IsPowerOfTwo(result, alignment)); + return result; +} + +void *ArenaPush(Arena *arena, U64 size, U64 alignment) +{ + void *result = ArenaPushNoZero(arena, size, alignment); + if (result) + memset(result, 0, size); + return result; +} + +void ArenaPopTo(Arena *arena, U64 pos) +{ + if (arena) { + assert(pos <= arena->last->pos); + arena->last->pos = pos; + } +} + +void ArenaPop(Arena *arena, U64 size) +{ + if (arena) { + assert(size <= arena->last->pos); + arena->last->pos -= size; + } +} + +void ArenaClear(Arena *arena) +{ + if (arena) + arena->last->pos = 0; +} + +// NOTE: Main ============================================================================ +int main(int, char**) +{ + SYSTEM_INFO info = {}; + GetSystemInfo(&info); + g_os.page_size = (U16)info.dwPageSize; + g_os.alloc_granularity = (U16)info.dwAllocationGranularity; + + Arena *arena = ArenaAlloc(GIGABYTES(1), ArenaFlag_ChainReserveCommit); + U64 *array = ArenaPushN(arena, U64, 1'000); + assert(array); + return 0; +}