Initial commit
This commit is contained in:
commit
d8cb10b432
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Build
|
19
build.bat
Normal file
19
build.bat
Normal file
@ -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
|
208
rfleury_arena.cpp
Normal file
208
rfleury_arena.cpp
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
// NOTE: Includes ==================================================================================
|
||||||
|
#define NOMINMAX
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user