Dqn/dqn_allocator.cpp

654 lines
25 KiB
C++
Raw Normal View History

/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
// $$ __$$\ $$ | $$ | $$ __$$\ $$ __$$\ $$ __$$\\__$$ __|$$ __$$\ $$ __$$\
// $$ / $$ |$$ | $$ | $$ / $$ |$$ / \__|$$ / $$ | $$ | $$ / $$ |$$ | $$ |
// $$$$$$$$ |$$ | $$ | $$ | $$ |$$ | $$$$$$$$ | $$ | $$ | $$ |$$$$$$$ |
// $$ __$$ |$$ | $$ | $$ | $$ |$$ | $$ __$$ | $$ | $$ | $$ |$$ __$$<
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$\ $$ | $$ | $$ | $$ | $$ |$$ | $$ |
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$$$$$ |\$$$$$$ |$$ | $$ | $$ | $$$$$$ |$$ | $$ |
// \__| \__|\________|\________|\______/ \______/ \__| \__| \__| \______/ \__| \__|
//
// dqn_allocator.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
2025-02-14 00:27:42 +11:00
// NOTE: [$AREN] DN_Arena /////////////////////////////////////////////////////////////////////////
DN_API DN_ArenaBlock *DN_Arena_BlockInit(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak)
{
2025-02-14 00:27:42 +11:00
DN_USize const page_size = g_dn_core->os_page_size;
DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE;
DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE;
real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size);
real_commit = DN_MIN(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve);
2025-02-14 00:27:42 +11:00
DN_ASSERTF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%I64u < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve);
DN_ASSERTF(page_size, "Call DN_Library_Init() to initialise the known page size");
2025-02-14 00:27:42 +11:00
DN_OSMemCommit mem_commit = real_reserve == real_commit ? DN_OSMemCommit_Yes : DN_OSMemCommit_No;
DN_ArenaBlock *result = DN_CAST(DN_ArenaBlock *)DN_OS_MemReserve(real_reserve, mem_commit, DN_OSMemPage_ReadWrite);
if (!result)
return result;
2025-02-14 00:27:42 +11:00
if (mem_commit == DN_OSMemCommit_No && !DN_OS_MemCommit(result, real_commit, DN_OSMemPage_ReadWrite)) {
DN_OS_MemRelease(result, real_reserve);
return result;
}
2025-02-14 00:27:42 +11:00
result->used = DN_ARENA_HEADER_SIZE;
result->commit = real_commit;
result->reserve = real_reserve;
if (track_alloc)
2025-02-14 00:27:42 +11:00
DN_Debug_TrackAlloc(result, result->reserve, alloc_can_leak);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaBlock *DN_Arena_BlockInitFlags(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
{
2025-02-14 00:27:42 +11:00
bool track_alloc = (flags & DN_ArenaFlags_NoAllocTrack) == 0;
bool alloc_can_leak = flags & DN_ArenaFlags_AllocCanLeak;
DN_ArenaBlock *result = DN_Arena_BlockInit(reserve, commit, track_alloc, alloc_can_leak);
if (result && ((flags & DN_ArenaFlags_NoPoison) == 0))
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *)result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE);
return result;
}
2025-02-14 00:27:42 +11:00
static void DN_Arena_UpdateStatsOnNewBlock_(DN_Arena *arena, DN_ArenaBlock const *block)
2024-08-01 13:34:36 +10:00
{
2025-02-14 00:27:42 +11:00
DN_ASSERT(arena);
2024-08-01 13:34:36 +10:00
if (block) {
arena->stats.info.used += block->used;
arena->stats.info.commit += block->commit;
arena->stats.info.reserve += block->reserve;
arena->stats.info.blocks += 1;
2025-02-14 00:27:42 +11:00
arena->stats.hwm.used = DN_MAX(arena->stats.hwm.used, arena->stats.info.used);
arena->stats.hwm.commit = DN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
arena->stats.hwm.reserve = DN_MAX(arena->stats.hwm.reserve, arena->stats.info.reserve);
arena->stats.hwm.blocks = DN_MAX(arena->stats.hwm.blocks, arena->stats.info.blocks);
2024-08-01 13:34:36 +10:00
}
}
2025-02-14 00:27:42 +11:00
DN_API DN_Arena DN_Arena_InitBuffer(void *buffer, DN_USize size, DN_ArenaFlags flags)
{
2025-02-14 00:27:42 +11:00
DN_ASSERT(buffer);
DN_ASSERTF(DN_ARENA_HEADER_SIZE < size, "Buffer (%zu bytes) too small, need atleast %zu bytes to store arena metadata", size, DN_ARENA_HEADER_SIZE);
DN_ASSERTF(DN_IsPowerOfTwo(size), "Buffer (%zu bytes) must be a power-of-two", size);
// NOTE: Init block
DN_ArenaBlock *block = DN_CAST(DN_ArenaBlock *) buffer;
block->commit = size;
block->reserve = size;
block->used = DN_ARENA_HEADER_SIZE;
if (block && ((flags & DN_ArenaFlags_NoPoison) == 0)) {
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *)block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE);
}
DN_Arena result = {};
result.flags = flags | DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_UserBuffer;
result.curr = block;
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
return result;
}
DN_API DN_Arena DN_Arena_InitSize(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
{
DN_Arena result = {};
result.flags = flags;
2025-02-14 00:27:42 +11:00
result.curr = DN_Arena_BlockInitFlags(reserve, commit, flags);
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
return result;
}
2025-02-14 00:27:42 +11:00
static void DN_Arena_BlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
{
2025-02-14 00:27:42 +11:00
DN_USize release_size = block->reserve;
if (DN_Bit_IsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
DN_Debug_TrackDealloc(block);
DN_ASAN_UnpoisonMemoryRegion(block, block->commit);
DN_OS_MemRelease(block, release_size);
}
2025-02-14 00:27:42 +11:00
DN_API void DN_Arena_Deinit(DN_Arena *arena)
{
2025-02-14 00:27:42 +11:00
for (DN_ArenaBlock *block = arena ? arena->curr : nullptr; block; ) {
DN_ArenaBlock *block_to_free = block;
block = block->prev;
2025-02-14 00:27:42 +11:00
DN_Arena_BlockDeinit_(arena, block_to_free);
}
2025-02-14 00:27:42 +11:00
if (arena)
*arena = {};
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_Arena_CommitTo(DN_Arena *arena, DN_U64 pos)
{
if (!arena || !arena->curr)
return false;
2025-02-14 00:27:42 +11:00
DN_ArenaBlock *curr = arena->curr;
if (pos <= curr->commit)
return true;
2025-02-14 00:27:42 +11:00
DN_U64 real_pos = pos;
if (!DN_CHECK(pos <= curr->reserve))
real_pos = curr->reserve;
2025-02-14 00:27:42 +11:00
DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, g_dn_core->os_page_size);
DN_USize commit_size = end_commit - curr->commit;
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
if (!DN_OS_MemCommit(commit_ptr, commit_size, DN_OSMemPage_ReadWrite))
return false;
2025-02-14 00:27:42 +11:00
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
if (poison)
2025-02-14 00:27:42 +11:00
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
curr->commit = end_commit;
return true;
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_Arena_Commit(DN_Arena *arena, DN_U64 size)
{
if (!arena || !arena->curr)
return false;
2025-02-14 00:27:42 +11:00
DN_U64 pos = arena->curr->commit + size;
bool result = DN_Arena_CommitTo(arena, pos);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_Arena_Grow(DN_Arena *arena, DN_U64 reserve, DN_U64 commit)
{
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
return false;
DN_ArenaBlock *new_block = DN_Arena_BlockInitFlags(reserve, commit, arena->flags);
if (new_block) {
new_block->prev = arena->curr;
arena->curr = new_block;
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
}
bool result = new_block;
return result;
}
DN_API void *DN_Arena_Alloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
{
if (!arena)
return nullptr;
2024-08-01 13:34:36 +10:00
if (!arena->curr) {
2025-02-14 00:27:42 +11:00
arena->curr = DN_Arena_BlockInitFlags(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, arena->flags);
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
2024-08-01 13:34:36 +10:00
}
if (!arena->curr)
return nullptr;
try_alloc_again:
2025-02-14 00:27:42 +11:00
DN_ArenaBlock *curr = arena->curr;
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
uint8_t real_align = poison ? DN_MAX(align, DN_ASAN_POISON_ALIGNMENT) : align;
DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0);
DN_U64 end_pos = offset_pos + size;
DN_U64 alloc_size = end_pos - curr->used;
if (end_pos > curr->reserve) {
2025-02-14 00:27:42 +11:00
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
return nullptr;
2025-02-14 00:27:42 +11:00
DN_USize new_reserve = DN_MAX(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE);
DN_USize new_commit = DN_MAX(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE);
if (!DN_Arena_Grow(arena, new_reserve, new_commit))
return nullptr;
goto try_alloc_again;
}
2025-02-14 00:27:42 +11:00
DN_USize prev_arena_commit = curr->commit;
if (end_pos > curr->commit) {
2025-02-14 00:27:42 +11:00
DN_ASSERT((arena->flags & DN_ArenaFlags_UserBuffer) == 0);
DN_USize end_commit = DN_AlignUpPowerOfTwo(end_pos, g_dn_core->os_page_size);
DN_USize commit_size = end_commit - curr->commit;
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
if (!DN_OS_MemCommit(commit_ptr, commit_size, DN_OSMemPage_ReadWrite))
return nullptr;
if (poison)
2025-02-14 00:27:42 +11:00
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
2024-08-01 13:34:36 +10:00
curr->commit = end_commit;
arena->stats.info.commit += commit_size;
2025-02-14 00:27:42 +11:00
arena->stats.hwm.commit = DN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
}
2025-02-14 00:27:42 +11:00
void *result = DN_CAST(char *) curr + offset_pos;
2024-08-01 13:34:36 +10:00
curr->used += alloc_size;
arena->stats.info.used += alloc_size;
2025-02-14 00:27:42 +11:00
arena->stats.hwm.used = DN_MAX(arena->stats.hwm.used, arena->stats.info.used);
DN_ASAN_UnpoisonMemoryRegion(result, size);
2025-02-14 00:27:42 +11:00
if (zero_mem == DN_ZeroMem_Yes) {
DN_USize reused_bytes = DN_MIN(prev_arena_commit - offset_pos, size);
DN_MEMSET(result, 0, reused_bytes);
}
2025-02-14 00:27:42 +11:00
DN_ASSERT(arena->stats.hwm.used >= arena->stats.info.used);
DN_ASSERT(arena->stats.hwm.commit >= arena->stats.info.commit);
DN_ASSERT(arena->stats.hwm.reserve >= arena->stats.info.reserve);
DN_ASSERT(arena->stats.hwm.blocks >= arena->stats.info.blocks);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API void *DN_Arena_AllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
{
2025-02-14 00:27:42 +11:00
DN_ArenaFlags prev_flags = arena->flags;
arena->flags |= (DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoPoison);
void *memory = DN_Arena_Alloc(arena, size, align, zero_mem);
arena->flags = prev_flags;
return memory;
}
2025-02-14 00:27:42 +11:00
DN_API void *DN_Arena_Copy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align)
{
if (!arena || !data || size == 0)
return nullptr;
2025-02-14 00:27:42 +11:00
void *result = DN_Arena_Alloc(arena, size, align, DN_ZeroMem_No);
if (result)
2025-02-14 00:27:42 +11:00
DN_MEMCPY(result, data, size);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API void DN_Arena_PopTo(DN_Arena *arena, DN_U64 init_used)
{
2024-08-01 13:34:36 +10:00
if (!arena || !arena->curr)
return;
2025-02-14 00:27:42 +11:00
DN_U64 used = DN_MAX(DN_ARENA_HEADER_SIZE, init_used);
DN_ArenaBlock *curr = arena->curr;
while (curr->reserve_sum >= used) {
2025-02-14 00:27:42 +11:00
DN_ArenaBlock *block_to_free = curr;
2024-08-01 13:34:36 +10:00
arena->stats.info.used -= block_to_free->used;
arena->stats.info.commit -= block_to_free->commit;
arena->stats.info.reserve -= block_to_free->reserve;
arena->stats.info.blocks -= 1;
2025-02-14 00:27:42 +11:00
if (arena->flags & DN_ArenaFlags_UserBuffer)
break;
curr = curr->prev;
2025-02-14 00:27:42 +11:00
DN_Arena_BlockDeinit_(arena, block_to_free);
}
2024-08-01 13:34:36 +10:00
arena->stats.info.used -= curr->used;
arena->curr = curr;
curr->used = used - curr->reserve_sum;
2025-02-14 00:27:42 +11:00
char *poison_ptr = (char *)curr + DN_AlignUpPowerOfTwo(curr->used, DN_ASAN_POISON_ALIGNMENT);
DN_USize poison_size = ((char *)curr + curr->commit) - poison_ptr;
DN_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
2024-08-01 13:34:36 +10:00
arena->stats.info.used += curr->used;
}
2025-02-14 00:27:42 +11:00
DN_API void DN_Arena_Pop(DN_Arena *arena, DN_U64 amount)
{
2025-02-14 00:27:42 +11:00
DN_ArenaBlock *curr = arena->curr;
DN_USize used_sum = curr->reserve_sum + curr->used;
if (!DN_CHECK(amount <= used_sum))
amount = used_sum;
2025-02-14 00:27:42 +11:00
DN_USize pop_to = used_sum - amount;
DN_Arena_PopTo(arena, pop_to);
}
2025-02-14 00:27:42 +11:00
DN_API DN_U64 DN_Arena_Pos(DN_Arena const *arena)
{
2025-02-14 00:27:42 +11:00
DN_U64 result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
return result;
}
2025-02-14 00:27:42 +11:00
DN_API void DN_Arena_Clear(DN_Arena *arena)
{
2025-02-14 00:27:42 +11:00
DN_Arena_PopTo(arena, 0);
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_Arena_OwnsPtr(DN_Arena const *arena, void *ptr)
{
bool result = false;
2025-02-14 00:27:42 +11:00
uintptr_t uint_ptr = DN_CAST(uintptr_t)ptr;
for (DN_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
uintptr_t begin = DN_CAST(uintptr_t) block + DN_ARENA_HEADER_SIZE;
uintptr_t end = begin + block->reserve;
result = uint_ptr >= begin && uint_ptr <= end;
}
return result;
}
2024-08-01 13:34:36 +10:00
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaStats DN_Arena_SumStatsArray(DN_ArenaStats const *array, DN_USize size)
2024-08-01 13:34:36 +10:00
{
2025-02-14 00:27:42 +11:00
DN_ArenaStats result = {};
DN_FOR_UINDEX(index, size) {
DN_ArenaStats stats = array[index];
2024-08-01 13:34:36 +10:00
result.info.used += stats.info.used;
result.info.commit += stats.info.commit;
result.info.reserve += stats.info.reserve;
result.info.blocks += stats.info.blocks;
2025-02-14 00:27:42 +11:00
result.hwm.used = DN_MAX(result.hwm.used, result.info.used);
result.hwm.commit = DN_MAX(result.hwm.commit, result.info.commit);
result.hwm.reserve = DN_MAX(result.hwm.reserve, result.info.reserve);
result.hwm.blocks = DN_MAX(result.hwm.blocks, result.info.blocks);
2024-08-01 13:34:36 +10:00
}
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaStats DN_Arena_SumStats(DN_ArenaStats lhs, DN_ArenaStats rhs)
2024-08-01 13:34:36 +10:00
{
2025-02-14 00:27:42 +11:00
DN_ArenaStats array[] = {lhs, rhs};
DN_ArenaStats result = DN_Arena_SumStatsArray(array, DN_ARRAY_UCOUNT(array));
2024-08-01 13:34:36 +10:00
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats(DN_Arena const *array, DN_USize size)
2024-08-01 13:34:36 +10:00
{
2025-02-14 00:27:42 +11:00
DN_ArenaStats result = {};
for (DN_USize index = 0; index < size; index++) {
DN_Arena const *arena = array + index;
result = DN_Arena_SumStats(result, arena->stats);
2024-08-01 13:34:36 +10:00
}
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin(DN_Arena *arena)
{
2025-02-14 00:27:42 +11:00
DN_ArenaTempMem result = {};
if (arena) {
2025-02-14 00:27:42 +11:00
DN_ArenaBlock *curr = arena->curr;
result = {arena, curr ? curr->reserve_sum + curr->used : 0};
}
return result;
};
2025-02-14 00:27:42 +11:00
DN_API void DN_Arena_TempMemEnd(DN_ArenaTempMem mem)
{
2025-02-14 00:27:42 +11:00
DN_Arena_PopTo(mem.arena, mem.used_sum);
};
2025-02-14 00:27:42 +11:00
DN_ArenaTempMemScope::DN_ArenaTempMemScope(DN_Arena *arena)
{
2025-02-14 00:27:42 +11:00
mem = DN_Arena_TempMemBegin(arena);
}
2025-02-14 00:27:42 +11:00
DN_ArenaTempMemScope::~DN_ArenaTempMemScope()
{
2025-02-14 00:27:42 +11:00
DN_Arena_TempMemEnd(mem);
}
2025-02-14 00:27:42 +11:00
// NOTE: [$POOL] DN_Pool //////////////////////////////////////////////////////////////////////////
DN_API DN_Pool DN_Pool_Init(DN_Arena *arena, uint8_t align)
{
2025-02-14 00:27:42 +11:00
DN_Pool result = {};
if (arena) {
result.arena = arena;
2025-02-14 00:27:42 +11:00
result.align = align ? align : DN_POOL_DEFAULT_ALIGN;
}
return result;
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_Pool_IsValid(DN_Pool const *pool)
{
bool result = pool && pool->arena && pool->align;
return result;
}
2025-02-14 00:27:42 +11:00
DN_API void *DN_Pool_Alloc(DN_Pool *pool, DN_USize size)
{
void *result = nullptr;
2025-02-14 00:27:42 +11:00
if (!DN_Pool_IsValid(pool))
return result;
2025-02-14 00:27:42 +11:00
DN_USize const required_size = sizeof(DN_PoolSlot) + pool->align + size;
DN_USize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. DN_PoolSlotSize_32B
DN_USize slot_index = 0;
if (required_size > 32) {
// NOTE: Round up if not PoT as the low bits are set.
2025-02-14 00:27:42 +11:00
DN_USize dist_to_next_msb = DN_CountLeadingZerosU64(required_size) + 1;
dist_to_next_msb -= DN_CAST(DN_USize)(!DN_IsPowerOfTwo(required_size));
2025-02-14 00:27:42 +11:00
DN_USize const register_size = sizeof(DN_USize) * 8;
DN_ASSERT(register_size >= dist_to_next_msb + size_to_slot_offset);
slot_index = register_size - dist_to_next_msb - size_to_slot_offset;
}
2025-02-14 00:27:42 +11:00
if (!DN_CHECKF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size"))
return result;
2025-02-14 00:27:42 +11:00
DN_USize slot_size_in_bytes = 1ULL << (slot_index + size_to_slot_offset);
DN_ASSERT(required_size <= (slot_size_in_bytes << 0));
DN_ASSERT(required_size >= (slot_size_in_bytes >> 1));
2025-02-14 00:27:42 +11:00
DN_PoolSlot *slot = nullptr;
if (pool->slots[slot_index]) {
slot = pool->slots[slot_index];
pool->slots[slot_index] = slot->next;
2025-02-14 00:27:42 +11:00
DN_MEMSET(slot->data, 0, size);
DN_ASSERT(DN_IsPowerOfTwoAligned(slot->data, pool->align));
} else {
2025-02-14 00:27:42 +11:00
void *bytes = DN_Arena_Alloc(pool->arena, slot_size_in_bytes, alignof(DN_PoolSlot), DN_ZeroMem_Yes);
slot = DN_CAST(DN_PoolSlot *) bytes;
// NOTE: The raw pointer is round up to the next 'pool->align'-ed
// address ensuring at least 1 byte of padding between the raw pointer
// and the pointer given to the user and that the user pointer is
// aligned to the pool's alignment.
//
// This allows us to smuggle 1 byte behind the user pointer that has
// the offset to the original pointer.
2025-02-14 00:27:42 +11:00
slot->data = DN_CAST(void *)DN_AlignDownPowerOfTwo(DN_CAST(uintptr_t)slot + sizeof(DN_PoolSlot) + pool->align, pool->align);
2025-02-14 00:27:42 +11:00
uintptr_t offset_to_original_ptr = DN_CAST(uintptr_t)slot->data - DN_CAST(uintptr_t)bytes;
DN_ASSERT(slot->data > bytes);
DN_ASSERT(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
// NOTE: Store the offset to the original pointer behind the user's
// pointer.
2025-02-14 00:27:42 +11:00
char *offset_to_original_storage = DN_CAST(char *)slot->data - 1;
DN_MEMCPY(offset_to_original_storage, &offset_to_original_ptr, 1);
}
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
// pointer gets returned which free list to return the pointer to.
result = slot->data;
2025-02-14 00:27:42 +11:00
slot->next = DN_CAST(DN_PoolSlot *)slot_index;
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_Str8 DN_Pool_AllocStr8FV(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args)
{
2025-02-14 00:27:42 +11:00
DN_Str8 result = {};
if (!DN_Pool_IsValid(pool))
return result;
2025-02-14 00:27:42 +11:00
DN_USize size_required = DN_CStr8_FVSize(fmt, args);
result.data = DN_CAST(char *) DN_Pool_Alloc(pool, size_required + 1);
if (result.data) {
result.size = size_required;
2025-02-14 00:27:42 +11:00
DN_VSNPRINTF(result.data, DN_CAST(int)(result.size + 1), fmt, args);
}
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_Str8 DN_Pool_AllocStr8F(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
2025-02-14 00:27:42 +11:00
DN_Str8 result = DN_Pool_AllocStr8FV(pool, fmt, args);
va_end(args);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_Str8 DN_Pool_AllocStr8Copy(DN_Pool *pool, DN_Str8 string)
{
2025-02-14 00:27:42 +11:00
DN_Str8 result = {};
if (!DN_Pool_IsValid(pool))
return result;
2025-02-14 00:27:42 +11:00
if (!DN_Str8_HasData(string))
return result;
2025-02-14 00:27:42 +11:00
char *data = DN_CAST(char *)DN_Pool_Alloc(pool, string.size + 1);
if (!data)
return result;
2025-02-14 00:27:42 +11:00
DN_MEMCPY(data, string.data, string.size);
data[string.size] = 0;
2025-02-14 00:27:42 +11:00
result = DN_Str8_Init(data, string.size);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API void DN_Pool_Dealloc(DN_Pool *pool, void *ptr)
{
2025-02-14 00:27:42 +11:00
if (!DN_Pool_IsValid(pool) || !ptr)
return;
2025-02-14 00:27:42 +11:00
DN_ASSERT(DN_Arena_OwnsPtr(pool->arena, ptr));
2024-08-01 13:34:36 +10:00
2025-02-14 00:27:42 +11:00
char const *one_byte_behind_ptr = DN_CAST(char *) ptr - 1;
DN_USize offset_to_original_ptr = 0;
DN_MEMCPY(&offset_to_original_ptr, one_byte_behind_ptr, 1);
DN_ASSERT(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
2025-02-14 00:27:42 +11:00
char *original_ptr = DN_CAST(char *) ptr - offset_to_original_ptr;
DN_PoolSlot *slot = DN_CAST(DN_PoolSlot *) original_ptr;
DN_PoolSlotSize slot_index = DN_CAST(DN_PoolSlotSize)(DN_CAST(uintptr_t) slot->next);
DN_ASSERT(slot_index < DN_PoolSlotSize_Count);
slot->next = pool->slots[slot_index];
pool->slots[slot_index] = slot;
}
2025-02-14 00:27:42 +11:00
DN_API void *DN_Pool_Copy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align)
2024-08-01 13:34:36 +10:00
{
if (!pool || !data || size == 0)
return nullptr;
// TODO: Hmm should align be part of the alloc interface in general? I'm not going to worry
// about this until we crash because of misalignment.
2025-02-14 00:27:42 +11:00
DN_ASSERT(pool->align >= align);
2024-08-01 13:34:36 +10:00
2025-02-14 00:27:42 +11:00
void *result = DN_Pool_Alloc(pool, size);
2024-08-01 13:34:36 +10:00
if (result)
2025-02-14 00:27:42 +11:00
DN_MEMCPY(result, data, size);
2024-08-01 13:34:36 +10:00
return result;
}
2025-02-14 00:27:42 +11:00
// NOTE: [$ACAT] DN_ArenaCatalog //////////////////////////////////////////////////////////////////
DN_API void DN_ArenaCatalog_Init(DN_ArenaCatalog *catalog, DN_Pool *pool)
{
catalog->pool = pool;
catalog->sentinel.next = &catalog->sentinel;
catalog->sentinel.prev = &catalog->sentinel;
}
2025-02-14 00:27:42 +11:00
DN_API DN_ArenaCatalogItem *DN_ArenaCatalog_Find(DN_ArenaCatalog *catalog, DN_Str8 label)
{
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
DN_ArenaCatalogItem *result = &catalog->sentinel;
for (DN_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
if (item->label == label) {
result = item;
break;
}
}
2025-02-14 00:27:42 +11:00
DN_TicketMutex_End(&catalog->ticket_mutex);
return result;
}
2025-02-14 00:27:42 +11:00
static void DN_ArenaCatalog_AddInternal_(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_Str8 label, bool arena_pool_allocated)
{
// NOTE: We could use an atomic for appending to the sentinel but it is such
// a rare operation to append to the catalog that we don't bother.
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
// NOTE: Create item in the catalog
2025-02-14 00:27:42 +11:00
DN_ArenaCatalogItem *result = DN_Pool_New(catalog->pool, DN_ArenaCatalogItem);
if (result) {
result->arena = arena;
result->label = label;
2024-08-01 13:34:36 +10:00
result->arena_pool_allocated = arena_pool_allocated;
// NOTE: Add to the catalog (linked list)
2025-02-14 00:27:42 +11:00
DN_ArenaCatalogItem *sentinel = &catalog->sentinel;
result->next = sentinel;
result->prev = sentinel->prev;
result->next->prev = result;
result->prev->next = result;
2025-02-14 00:27:42 +11:00
DN_Atomic_AddU32(&catalog->arena_count, 1);
}
2025-02-14 00:27:42 +11:00
DN_TicketMutex_End(&catalog->ticket_mutex);
}
2025-02-14 00:27:42 +11:00
DN_API void DN_ArenaCatalog_AddF(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
DN_TicketMutex_End(&catalog->ticket_mutex);
va_end(args);
2025-02-14 00:27:42 +11:00
DN_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
}
2025-02-14 00:27:42 +11:00
DN_API void DN_ArenaCatalog_AddFV(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args)
{
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
DN_TicketMutex_End(&catalog->ticket_mutex);
DN_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
}
2025-02-14 00:27:42 +11:00
DN_API DN_Arena *DN_ArenaCatalog_AllocFV(DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, va_list args)
{
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
DN_Arena *result = DN_Pool_New(catalog->pool, DN_Arena);
DN_TicketMutex_End(&catalog->ticket_mutex);
2025-02-14 00:27:42 +11:00
*result = DN_Arena_InitSize(reserve, commit, arena_flags);
DN_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API DN_Arena *DN_ArenaCatalog_AllocF(DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, ...)
{
2024-08-01 13:34:36 +10:00
va_list args;
va_start(args, fmt);
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
DN_Arena *result = DN_Pool_New(catalog->pool, DN_Arena);
DN_TicketMutex_End(&catalog->ticket_mutex);
2024-08-01 13:34:36 +10:00
va_end(args);
2025-02-14 00:27:42 +11:00
*result = DN_Arena_InitSize(reserve, commit, arena_flags);
DN_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
return result;
}
2025-02-14 00:27:42 +11:00
DN_API bool DN_ArenaCatalog_Erase(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_ArenaCatalogFreeArena free_arena)
{
2024-08-01 13:34:36 +10:00
bool result = false;
2025-02-14 00:27:42 +11:00
DN_TicketMutex_Begin(&catalog->ticket_mutex);
for (DN_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
2024-08-01 13:34:36 +10:00
if (item->arena == arena) {
item->next->prev = item->prev;
item->prev->next = item->next;
if (item->arena_pool_allocated) {
2025-02-14 00:27:42 +11:00
if (free_arena == DN_ArenaCatalogFreeArena_Yes)
DN_Arena_Deinit(item->arena);
DN_Pool_Dealloc(catalog->pool, item->arena);
2024-08-01 13:34:36 +10:00
}
2025-02-14 00:27:42 +11:00
DN_Pool_Dealloc(catalog->pool, item->label.data);
DN_Pool_Dealloc(catalog->pool, item);
2024-08-01 13:34:36 +10:00
result = true;
break;
}
}
2025-02-14 00:27:42 +11:00
DN_TicketMutex_End(&catalog->ticket_mutex);
return result;
}