From ed2020edf163d1a017ec2989da4d28865afe74ae Mon Sep 17 00:00:00 2001 From: doyle Date: Mon, 7 Aug 2023 22:53:41 +1000 Subject: [PATCH] Implement simple array --- build.bat | 2 +- rfleury_arena.cpp | 105 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/build.bat b/build.bat index 149350b..49bef58 100644 --- a/build.bat +++ b/build.bat @@ -6,7 +6,7 @@ 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 compile_flags=%code_dir%\rfleury_arena.cpp /W4 /Z7 /MD /EHsc /nologo /fsanitize=address set link_flags=/link /incremental:no set msvc_cmd=cl %compile_flags% /Forfleury_arena /Ferfleury_arena %link_flags% diff --git a/rfleury_arena.cpp b/rfleury_arena.cpp index 06b1621..69013ce 100644 --- a/rfleury_arena.cpp +++ b/rfleury_arena.cpp @@ -5,6 +5,7 @@ #include #include +#include // NOTE: Types ===================================================================================== typedef uint16_t U16; @@ -16,8 +17,8 @@ typedef uint64_t U64; struct OS { - U16 page_size; - U16 alloc_granularity; + U32 page_size; + U32 alloc_granularity; }; OS g_os; @@ -54,17 +55,55 @@ 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 ArenaPushAlignment(Arena *arena, 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) & ((uintptr_t)pot - 1)) == 0) +#define AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1)) +#define AlignDownPowerOfTwo(value, pot) (((uintptr_t)value) & (~((uintptr_t)pot - 1))) +#define ArenaBlockMetadataSize(block) sizeof(ArenaBlock) + (block->next == block->prev ? sizeof(Arena) : 0) -#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))) +template +struct Array +{ + T *data; + size_t size; + size_t cap; + Arena *arena; + + static Array Init(U64 capacity); + + T *begin() { return data; } + T *end() { return data + size; } + T const *begin() const { return data; } + T const *end() const { return data + size; } + + T *PushBack(const T& item); +}; + +template +Array Array::Init(U64 capacity) +{ + Array result = {}; + result.arena = ArenaAlloc(capacity * sizeof(T), ArenaFlag_ReserveCommit); + result.cap = capacity; + result.data = (T *)ArenaPush(result.arena, 0 /*size*/, alignof(T)); + return result; +} + +template +T *Array::PushBack(const T& item) +{ + T* result = ArenaPushN(arena, T, 1); + if (result) { + size++; + *result = item; + } + return result; +} // NOTE: Implementation ============================================================================ Arena *ArenaAlloc(U64 initial_cap, U32 flags) @@ -88,6 +127,7 @@ Arena *ArenaAlloc(U64 initial_cap, U32 flags) // NOTE: Create arena and block Arena *result = (Arena *)ptr; + result->flags = flags; ptr += sizeof(*result); ArenaBlock *block = (ArenaBlock *)ptr; ptr += sizeof(*block); @@ -124,7 +164,12 @@ void *ArenaPushNoZero(Arena *arena, U64 size, U64 alignment) return result; assert(IsPowerOfTwo(alignment, alignment)); // Enforce PoT - while ((arena->last->pos + size) > arena->last->cap) { + U64 new_block_pos = 0; + for (;;) { + new_block_pos = AlignUpPowerOfTwo(arena->last->pos, alignment) + size; + if (new_block_pos <= arena->last->cap) + break; + if ((arena->flags & ArenaFlag_Chain) == 0) return result; @@ -139,24 +184,22 @@ void *ArenaPushNoZero(Arena *arena, U64 size, U64 alignment) block->next = arena->last->next; block->prev->next = block; block->next->prev = block; + arena->last = 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; + block->pos = new_block_pos; 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)); + void *commit_ptr = (void *)AlignUpPowerOfTwo((char *)block->data + block->commit, 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)); + block->commit += commit_size; } assert(IsPowerOfTwo(result, alignment)); @@ -198,11 +241,35 @@ int main(int, char**) { SYSTEM_INFO info = {}; GetSystemInfo(&info); - g_os.page_size = (U16)info.dwPageSize; - g_os.alloc_granularity = (U16)info.dwAllocationGranularity; + g_os.page_size = info.dwPageSize; + g_os.alloc_granularity = info.dwAllocationGranularity; + printf("Page Size: %u bytes\n", g_os.page_size); + printf("Allocation Granularity: %u bytes\n", g_os.alloc_granularity); - Arena *arena = ArenaAlloc(GIGABYTES(1), ArenaFlag_ChainReserveCommit); - U64 *array = ArenaPushN(arena, U64, 1'000); - assert(array); + Arena *arena = ArenaAlloc(g_os.page_size - 8, ArenaFlag_ChainReserveCommit); + { + U64 u64s_in_a_page = g_os.page_size / sizeof(U64); + U64 *u64s_array = ArenaPushN(arena, U64, u64s_in_a_page); + assert(u64s_array); + memset(u64s_array, 0xFA, u64s_in_a_page * sizeof(U64)); + } + + { + U64 u64s_in_straddled_page = (g_os.page_size / sizeof(U64)) + 1; + U64 *u64s_array = ArenaPushN(arena, U64, u64s_in_straddled_page); + assert(u64s_array); + memset(u64s_array, 0xFB, u64s_in_straddled_page * sizeof(U64)); + } + + { + Array array = Array::Init(TERABYTES(1) / sizeof(U64)); + for (size_t index = 0; index < 100'000; index++) + array.PushBack(1ULL); + assert(array.size == 100'000); + assert(array.cap == TERABYTES(1) / sizeof(U64)); + memset(array.data, 0xFC, array.size * sizeof(*array.data)); + } + + ArenaPopTo(arena, 0); return 0; }