Pull in changes from internal project

This commit is contained in:
doylet 2024-08-01 13:34:36 +10:00
parent 765b8255f7
commit 6b403eae71
32 changed files with 2317 additions and 1135 deletions

View File

@ -1,3 +1,4 @@
/*
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\ // $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\
@ -12,6 +13,7 @@
// dqn_cpp_file.h -- Functions to emit C++ formatted code // dqn_cpp_file.h -- Functions to emit C++ formatted code
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
*/
#if !defined(DQN_CPP_FILE_H) #if !defined(DQN_CPP_FILE_H)
#define DQN_CPP_FILE_H #define DQN_CPP_FILE_H

View File

@ -13,7 +13,7 @@ pushd Build
REM O2 Optimisation Level 2 REM O2 Optimisation Level 2
REM Oi Use CPU Intrinsics REM Oi Use CPU Intrinsics
REM Z7 Combine multi-debug files to one debug file REM Z7 Combine multi-debug files to one debug file
set common_flags=-D DQN_WITH_UNIT_TESTS -D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF %script_dir%\dqn.cpp set common_flags=-D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.cpp
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo

14
dqn.cpp
View File

@ -21,7 +21,17 @@
#if !defined(DQN_NO_METADESK) #if !defined(DQN_NO_METADESK)
DQN_MSVC_WARNING_PUSH DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<function>': unreferenced function with internal linkage has been removed DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<function>': unreferenced function with internal linkage has been removed
DQN_GCC_WARNING_PUSH
DQN_GCC_WARNING_DISABLE(-Wwrite-strings)
DQN_GCC_WARNING_DISABLE(-Wunused-but-set-variable)
DQN_GCC_WARNING_DISABLE(-Wsign-compare)
DQN_GCC_WARNING_DISABLE(-Wunused-function)
DQN_GCC_WARNING_DISABLE(-Wunused-result)
#include "External/metadesk/md.c" #include "External/metadesk/md.c"
DQN_GCC_WARNING_POP
DQN_MSVC_WARNING_POP DQN_MSVC_WARNING_POP
#endif #endif
#define DQN_CPP_FILE_IMPLEMENTATION #define DQN_CPP_FILE_IMPLEMENTATION
@ -34,13 +44,13 @@
#endif #endif
#include "dqn_base.cpp" #include "dqn_base.cpp"
#include "dqn_thread_context.cpp"
#include "dqn_external.cpp" #include "dqn_external.cpp"
#include "dqn_allocator.cpp" #include "dqn_allocator.cpp"
#include "dqn_debug.cpp" #include "dqn_debug.cpp"
#include "dqn_string.cpp" #include "dqn_string.cpp"
#include "dqn_containers.cpp" #include "dqn_containers.cpp"
#include "dqn_type_info.cpp" #include "dqn_type_info.cpp"
#include "dqn_os.cpp"
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64) #if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
#include "dqn_os_posix.cpp" #include "dqn_os_posix.cpp"
@ -50,7 +60,7 @@
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs #error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif #endif
#include "dqn_os.cpp" #include "dqn_tls.cpp"
#include "dqn_math.cpp" #include "dqn_math.cpp"
#include "dqn_hash.cpp" #include "dqn_hash.cpp"
#include "dqn_helpers.cpp" #include "dqn_helpers.cpp"

11
dqn.h
View File

@ -126,7 +126,7 @@
// //
// Allocations are stored in a global hash-table and their respective stack // Allocations are stored in a global hash-table and their respective stack
// traces for the allocation location. Memory leaks can be dumped at the end // traces for the allocation location. Memory leaks can be dumped at the end
// of the program or some epoch by calling Dqn_Library_DumpLeaks() // of the program or some epoch by calling Dqn_Debug_DumpLeaks()
// //
// #define DQN_LEAK_TRACKING // #define DQN_LEAK_TRACKING
// //
@ -300,17 +300,14 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#define DQN_UNDO_CRT_SECURE_NO_WARNINGS #define DQN_UNDO_CRT_SECURE_NO_WARNINGS
#endif #endif
#define MD_DEFAULT_SPRINTF 0
#define MD_IMPL_Vsnprintf DQN_VSNPRINTF
#include "External/metadesk/md.h" #include "External/metadesk/md.h"
#if defined(DQN_UNDO_CRT_SECURE_NO_WARNINGS) #if defined(DQN_UNDO_CRT_SECURE_NO_WARNINGS)
#undef _CRT_SECURE_NO_WARNINGS #undef _CRT_SECURE_NO_WARNINGS
#endif #endif
#endif #endif
// Metadesk includes 'stb_sprintf.h' already
#if !defined(DQN_STB_SPRINTF_HEADER_ONLY)
#define DQN_STB_SPRINTF_HEADER_ONLY
#endif
// Metadesk includes Windows.h // Metadesk includes Windows.h
#define DQN_NO_WIN32_MIN_HEADER #define DQN_NO_WIN32_MIN_HEADER
#endif #endif
@ -321,7 +318,7 @@
#include "dqn_win32.h" #include "dqn_win32.h"
#endif #endif
#include "dqn_allocator.h" #include "dqn_allocator.h"
#include "dqn_thread_context.h" #include "dqn_tls.h"
#include "dqn_debug.h" #include "dqn_debug.h"
#include "dqn_string.h" #include "dqn_string.h"
#include "dqn_containers.h" #include "dqn_containers.h"

View File

@ -56,11 +56,28 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInitFlags(uint64_t reserve, uint64_t comm
return result; return result;
} }
static void Dqn_Arena_UpdateStatsOnNewBlock_(Dqn_Arena *arena, Dqn_ArenaBlock const *block)
{
DQN_ASSERT(arena);
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;
arena->stats.hwm.used = DQN_MAX(arena->stats.hwm.used, arena->stats.info.used);
arena->stats.hwm.commit = DQN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
arena->stats.hwm.reserve = DQN_MAX(arena->stats.hwm.reserve, arena->stats.info.reserve);
arena->stats.hwm.blocks = DQN_MAX(arena->stats.hwm.blocks, arena->stats.info.blocks);
}
}
DQN_API Dqn_Arena Dqn_Arena_InitSize(uint64_t reserve, uint64_t commit, uint8_t flags) DQN_API Dqn_Arena Dqn_Arena_InitSize(uint64_t reserve, uint64_t commit, uint8_t flags)
{ {
Dqn_Arena result = {}; Dqn_Arena result = {};
result.flags = flags; result.flags = flags;
result.curr = Dqn_Arena_BlockInitFlags(reserve, commit, flags); result.curr = Dqn_Arena_BlockInitFlags(reserve, commit, flags);
Dqn_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
return result; return result;
} }
@ -123,8 +140,10 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
if (!arena) if (!arena)
return nullptr; return nullptr;
if (!arena->curr) if (!arena->curr) {
arena->curr = Dqn_Arena_BlockInitFlags(DQN_ARENA_RESERVE_SIZE, DQN_ARENA_COMMIT_SIZE, arena->flags); arena->curr = Dqn_Arena_BlockInitFlags(DQN_ARENA_RESERVE_SIZE, DQN_ARENA_COMMIT_SIZE, arena->flags);
Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
}
if (!arena->curr) if (!arena->curr)
return nullptr; return nullptr;
@ -147,6 +166,7 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
new_block->prev = arena->curr; new_block->prev = arena->curr;
arena->curr = new_block; arena->curr = new_block;
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve; new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
goto try_alloc_again; goto try_alloc_again;
} }
@ -159,11 +179,16 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
return nullptr; return nullptr;
if (poison) if (poison)
Dqn_ASAN_PoisonMemoryRegion(commit_ptr, commit_size); Dqn_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
curr->commit = end_commit; curr->commit = end_commit;
arena->stats.info.commit += commit_size;
arena->stats.hwm.commit = DQN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
} }
void *result = DQN_CAST(char *) curr + offset_pos; void *result = DQN_CAST(char *) curr + offset_pos;
curr->used = end_pos; Dqn_usize alloc_size = end_pos - curr->used;
curr->used += alloc_size;
arena->stats.info.used += alloc_size;
arena->stats.hwm.used = DQN_MAX(arena->stats.hwm.used, arena->stats.info.used);
Dqn_ASAN_UnpoisonMemoryRegion(result, size); Dqn_ASAN_UnpoisonMemoryRegion(result, size);
if (zero_mem == Dqn_ZeroMem_Yes) { if (zero_mem == Dqn_ZeroMem_Yes) {
@ -171,6 +196,10 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
DQN_MEMSET(result, 0, reused_bytes); DQN_MEMSET(result, 0, reused_bytes);
} }
DQN_ASSERT(arena->stats.hwm.used >= arena->stats.info.used);
DQN_ASSERT(arena->stats.hwm.commit >= arena->stats.info.commit);
DQN_ASSERT(arena->stats.hwm.reserve >= arena->stats.info.reserve);
DQN_ASSERT(arena->stats.hwm.blocks >= arena->stats.info.blocks);
return result; return result;
} }
@ -195,21 +224,27 @@ DQN_API void *Dqn_Arena_Copy(Dqn_Arena *arena, void const *data, uint64_t size,
DQN_API void Dqn_Arena_PopTo(Dqn_Arena *arena, uint64_t init_used) DQN_API void Dqn_Arena_PopTo(Dqn_Arena *arena, uint64_t init_used)
{ {
if (!arena) if (!arena || !arena->curr)
return; return;
uint64_t used = DQN_MAX(DQN_ARENA_HEADER_SIZE, init_used); uint64_t used = DQN_MAX(DQN_ARENA_HEADER_SIZE, init_used);
Dqn_ArenaBlock *curr = arena->curr; Dqn_ArenaBlock *curr = arena->curr;
while (curr->reserve_sum >= used) { while (curr->reserve_sum >= used) {
Dqn_ArenaBlock *block_to_free = curr; Dqn_ArenaBlock *block_to_free = curr;
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;
curr = curr->prev; curr = curr->prev;
Dqn_Arena_BlockDeinit_(arena, block_to_free); Dqn_Arena_BlockDeinit_(arena, block_to_free);
} }
arena->curr = curr; arena->stats.info.used -= curr->used;
curr->used = used - curr->reserve_sum; arena->curr = curr;
char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT); curr->used = used - curr->reserve_sum;
Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr; char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT);
Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr;
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, poison_size); Dqn_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
arena->stats.info.used += curr->used;
} }
DQN_API void Dqn_Arena_Pop(Dqn_Arena *arena, uint64_t amount) DQN_API void Dqn_Arena_Pop(Dqn_Arena *arena, uint64_t amount)
@ -237,7 +272,7 @@ DQN_API bool Dqn_Arena_OwnsPtr(Dqn_Arena const *arena, void *ptr)
{ {
bool result = false; bool result = false;
uintptr_t uint_ptr = DQN_CAST(uintptr_t)ptr; uintptr_t uint_ptr = DQN_CAST(uintptr_t)ptr;
for (Dqn_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; ) { for (Dqn_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
uintptr_t begin = DQN_CAST(uintptr_t) block + DQN_ARENA_HEADER_SIZE; uintptr_t begin = DQN_CAST(uintptr_t) block + DQN_ARENA_HEADER_SIZE;
uintptr_t end = begin + block->reserve; uintptr_t end = begin + block->reserve;
result = uint_ptr >= begin && uint_ptr <= end; result = uint_ptr >= begin && uint_ptr <= end;
@ -245,6 +280,42 @@ DQN_API bool Dqn_Arena_OwnsPtr(Dqn_Arena const *arena, void *ptr)
return result; return result;
} }
DQN_API Dqn_ArenaStats Dqn_Arena_SumStatsArray(Dqn_ArenaStats const *array, Dqn_usize size)
{
Dqn_ArenaStats result = {};
DQN_FOR_UINDEX(index, size) {
Dqn_ArenaStats stats = array[index];
result.info.used += stats.info.used;
result.info.commit += stats.info.commit;
result.info.reserve += stats.info.reserve;
result.info.blocks += stats.info.blocks;
result.hwm.used = DQN_MAX(result.hwm.used, result.info.used);
result.hwm.commit = DQN_MAX(result.hwm.commit, result.info.commit);
result.hwm.reserve = DQN_MAX(result.hwm.reserve, result.info.reserve);
result.hwm.blocks = DQN_MAX(result.hwm.blocks, result.info.blocks);
}
return result;
}
DQN_API Dqn_ArenaStats Dqn_Arena_SumStats(Dqn_ArenaStats lhs, Dqn_ArenaStats rhs)
{
Dqn_ArenaStats array[] = {lhs, rhs};
Dqn_ArenaStats result = Dqn_Arena_SumStatsArray(array, DQN_ARRAY_UCOUNT(array));
return result;
}
DQN_API Dqn_ArenaStats Dqn_Arena_SumArenaArrayToStats(Dqn_Arena const *array, Dqn_usize size)
{
Dqn_ArenaStats result = {};
for (Dqn_usize index = 0; index < size; index++) {
Dqn_Arena const *arena = array + index;
result = Dqn_Arena_SumStats(result, arena->stats);
}
return result;
}
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin(Dqn_Arena *arena) DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin(Dqn_Arena *arena)
{ {
Dqn_ArenaTempMem result = {}; Dqn_ArenaTempMem result = {};
@ -299,13 +370,8 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
Dqn_usize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. Dqn_ChunkPoolSlotSize_32B Dqn_usize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. Dqn_ChunkPoolSlotSize_32B
Dqn_usize slot_index = 0; Dqn_usize slot_index = 0;
if (required_size > 32) { if (required_size > 32) {
#if defined(DQN_OS_WIN32)
Dqn_usize dist_to_next_msb = __lzcnt64(required_size) + 1;
#else
Dqn_usize dist_to_next_msb = __builtin_clzll(required_size) + 1;
#endif
// NOTE: Round up if not PoT as the low bits are set. // NOTE: Round up if not PoT as the low bits are set.
Dqn_usize dist_to_next_msb = Dqn_CountLeadingZerosU64(required_size) + 1;
dist_to_next_msb -= DQN_CAST(Dqn_usize)(!Dqn_IsPowerOfTwo(required_size)); dist_to_next_msb -= DQN_CAST(Dqn_usize)(!Dqn_IsPowerOfTwo(required_size));
Dqn_usize const register_size = sizeof(Dqn_usize) * 8; Dqn_usize const register_size = sizeof(Dqn_usize) * 8;
@ -401,14 +467,17 @@ DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy(Dqn_ChunkPool *pool, Dqn_Str8 strin
DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr) DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr)
{ {
if (!Dqn_ChunkPool_IsValid(pool)) if (!Dqn_ChunkPool_IsValid(pool) || !ptr)
return; return;
Dqn_usize offset_to_original_ptr = 0; DQN_ASSERT(Dqn_Arena_OwnsPtr(pool->arena, ptr));
DQN_MEMCPY(&offset_to_original_ptr, &(DQN_CAST(char *)ptr)[-1], 1);
char const *one_byte_behind_ptr = DQN_CAST(char *) ptr - 1;
Dqn_usize offset_to_original_ptr = 0;
DQN_MEMCPY(&offset_to_original_ptr, one_byte_behind_ptr, 1);
DQN_ASSERT(offset_to_original_ptr <= sizeof(Dqn_ChunkPoolSlot) + pool->align); DQN_ASSERT(offset_to_original_ptr <= sizeof(Dqn_ChunkPoolSlot) + pool->align);
char *original_ptr = DQN_CAST(char *)ptr - offset_to_original_ptr; char *original_ptr = DQN_CAST(char *)ptr - offset_to_original_ptr;
Dqn_ChunkPoolSlot *slot = DQN_CAST(Dqn_ChunkPoolSlot *)original_ptr; Dqn_ChunkPoolSlot *slot = DQN_CAST(Dqn_ChunkPoolSlot *)original_ptr;
Dqn_ChunkPoolSlotSize slot_index = DQN_CAST(Dqn_ChunkPoolSlotSize)(DQN_CAST(uintptr_t)slot->next); Dqn_ChunkPoolSlotSize slot_index = DQN_CAST(Dqn_ChunkPoolSlotSize)(DQN_CAST(uintptr_t)slot->next);
DQN_ASSERT(slot_index < Dqn_ChunkPoolSlotSize_Count); DQN_ASSERT(slot_index < Dqn_ChunkPoolSlotSize_Count);
@ -417,6 +486,20 @@ DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr)
pool->slots[slot_index] = slot; pool->slots[slot_index] = slot;
} }
DQN_API void *Dqn_ChunkPool_Copy(Dqn_ChunkPool *pool, void const *data, uint64_t size, uint8_t align)
{
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.
DQN_ASSERT(pool->align >= align);
void *result = Dqn_ChunkPool_Alloc(pool, size);
if (result)
DQN_MEMCPY(result, data, size);
return result;
}
// NOTE: [$ACAT] Dqn_ArenaCatalog ////////////////////////////////////////////////////////////////// // NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool) DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool)
@ -440,7 +523,7 @@ DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, D
return result; return result;
} }
DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label) static void Dqn_ArenaCatalog_AddInternal_(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label, bool arena_pool_allocated)
{ {
// NOTE: We could use an atomic for appending to the sentinel but it is such // 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. // a rare operation to append to the catalog that we don't bother.
@ -451,6 +534,7 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *
if (result) { if (result) {
result->arena = arena; result->arena = arena;
result->label = label; result->label = label;
result->arena_pool_allocated = arena_pool_allocated;
// NOTE: Add to the catalog (linked list) // NOTE: Add to the catalog (linked list)
Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel; Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel;
@ -463,14 +547,6 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *
Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_TicketMutex_End(&catalog->ticket_mutex);
} }
DQN_API void Dqn_ArenaCatalog_AddLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
{
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy);
}
DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...) DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
va_list args; va_list args;
@ -479,7 +555,7 @@ DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena,
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_TicketMutex_End(&catalog->ticket_mutex);
va_end(args); va_end(args);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); Dqn_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
} }
DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args) DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
@ -487,35 +563,18 @@ DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena,
Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); Dqn_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
}
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelRef(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
{
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
Dqn_ArenaCatalog_AddLabelRef(catalog, result, label);
return result;
}
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
{
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy);
return result;
} }
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args) DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args)
{ {
Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
Dqn_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
return result; return result;
} }
@ -525,8 +584,34 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize
va_start(args, fmt); va_start(args, fmt);
Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
va_end(args); va_end(args);
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
Dqn_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
return result;
}
DQN_API bool Dqn_ArenaCatalog_Erase(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena)
{
bool result = false;
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
if (item->arena == arena) {
item->next->prev = item->prev;
item->prev->next = item->next;
if (item->arena_pool_allocated) {
if (free_arena == Dqn_ArenaCatalogFreeArena_Yes)
Dqn_Arena_Deinit(item->arena);
Dqn_ChunkPool_Dealloc(catalog->pool, item->arena);
}
Dqn_ChunkPool_Dealloc(catalog->pool, item->label.data);
Dqn_ChunkPool_Dealloc(catalog->pool, item);
result = true;
break;
}
}
Dqn_TicketMutex_End(&catalog->ticket_mutex);
return result; return result;
} }

View File

@ -50,11 +50,26 @@ enum Dqn_ArenaFlag
Dqn_ArenaFlag_AllocCanLeak = 1 << 3, Dqn_ArenaFlag_AllocCanLeak = 1 << 3,
}; };
struct Dqn_ArenaInfo
{
uint64_t used;
uint64_t commit;
uint64_t reserve;
uint64_t blocks;
};
struct Dqn_ArenaStats
{
Dqn_ArenaInfo info;
Dqn_ArenaInfo hwm;
};
struct Dqn_Arena struct Dqn_Arena
{ {
Dqn_ArenaBlock *curr; Dqn_ArenaBlock *curr;
uint8_t flags; Dqn_ArenaStats stats;
Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use. Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use.
uint8_t flags;
}; };
struct Dqn_ArenaTempMem struct Dqn_ArenaTempMem
@ -131,6 +146,7 @@ struct Dqn_ArenaCatalogItem
{ {
Dqn_Arena *arena; Dqn_Arena *arena;
Dqn_Str8 label; Dqn_Str8 label;
bool arena_pool_allocated;
Dqn_ArenaCatalogItem *next; Dqn_ArenaCatalogItem *next;
Dqn_ArenaCatalogItem *prev; Dqn_ArenaCatalogItem *prev;
}; };
@ -143,6 +159,12 @@ struct Dqn_ArenaCatalog
uint16_t arena_count; uint16_t arena_count;
}; };
enum Dqn_ArenaCatalogFreeArena
{
Dqn_ArenaCatalogFreeArena_No,
Dqn_ArenaCatalogFreeArena_Yes,
};
// NOTE: [$AREN] Dqn_Arena ///////////////////////////////////////////////////////////////////////// // NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Arena Dqn_Arena_InitSize (uint64_t reserve, uint64_t commit, uint8_t flags); DQN_API Dqn_Arena Dqn_Arena_InitSize (uint64_t reserve, uint64_t commit, uint8_t flags);
DQN_API void Dqn_Arena_Deinit (Dqn_Arena *arena); DQN_API void Dqn_Arena_Deinit (Dqn_Arena *arena);
@ -156,6 +178,9 @@ DQN_API void Dqn_Arena_Pop (Dqn_Arena *arena,
DQN_API uint64_t Dqn_Arena_Pos (Dqn_Arena const *arena); DQN_API uint64_t Dqn_Arena_Pos (Dqn_Arena const *arena);
DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena); DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena);
DQN_API bool Dqn_Arena_OwnsPtr (Dqn_Arena const *arena, void *ptr); DQN_API bool Dqn_Arena_OwnsPtr (Dqn_Arena const *arena, void *ptr);
DQN_API Dqn_ArenaStats Dqn_Arena_SumStatsArray (Dqn_ArenaStats const *array, Dqn_usize size);
DQN_API Dqn_ArenaStats Dqn_Arena_SumStats (Dqn_ArenaStats lhs, Dqn_ArenaStats rhs);
DQN_API Dqn_ArenaStats Dqn_Arena_SumArenaArrayToStats (Dqn_Arena const *array, Dqn_usize size);
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena); DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena);
DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem); DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem);
#define Dqn_Arena_New(arena, T, zero_mem) (T *)Dqn_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem) #define Dqn_Arena_New(arena, T, zero_mem) (T *)Dqn_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
@ -164,7 +189,6 @@ DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem m
#define Dqn_Arena_NewArrayCopy(arena, T, src, count) (T *)Dqn_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T)) #define Dqn_Arena_NewArrayCopy(arena, T, src, count) (T *)Dqn_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T))
// NOTE: [$CHUN] Dqn_ChunkPool ///////////////////////////////////////////////////////////////////// // NOTE: [$CHUN] Dqn_ChunkPool /////////////////////////////////////////////////////////////////////
#define Dqn_ChunkPool_New(pool, T) (T *)Dqn_ChunkPool_Alloc(pool, sizeof(T))
DQN_API Dqn_ChunkPool Dqn_ChunkPool_Init (Dqn_Arena *arena, uint8_t align); DQN_API Dqn_ChunkPool Dqn_ChunkPool_Init (Dqn_Arena *arena, uint8_t align);
DQN_API bool Dqn_ChunkPool_IsValid (Dqn_ChunkPool const *pool); DQN_API bool Dqn_ChunkPool_IsValid (Dqn_ChunkPool const *pool);
DQN_API void * Dqn_ChunkPool_Alloc (Dqn_ChunkPool *pool, Dqn_usize size); DQN_API void * Dqn_ChunkPool_Alloc (Dqn_ChunkPool *pool, Dqn_usize size);
@ -172,15 +196,18 @@ DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8FV (Dqn_ChunkPool *poo
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8F (Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8F (Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy (Dqn_ChunkPool *pool, Dqn_Str8 string); DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy (Dqn_ChunkPool *pool, Dqn_Str8 string);
DQN_API void Dqn_ChunkPool_Dealloc (Dqn_ChunkPool *pool, void *ptr); DQN_API void Dqn_ChunkPool_Dealloc (Dqn_ChunkPool *pool, void *ptr);
DQN_API void * Dqn_ChunkPool_Copy (Dqn_ChunkPool *pool, void const *data, uint64_t size, uint8_t align);
#define Dqn_ChunkPool_New(pool, T) (T *)Dqn_ChunkPool_Alloc(pool, sizeof(T))
#define Dqn_ChunkPool_NewArray(pool, T, count) (T *)Dqn_ChunkPool_Alloc(pool, count * sizeof(T))
#define Dqn_ChunkPool_NewCopy(arena, T, src) (T *)Dqn_ChunkPool_Copy (arena, (src), sizeof(T), alignof(T))
#define Dqn_ChunkPool_NewArrayCopy(arena, T, src, count) (T *)Dqn_ChunkPool_Copy (arena, (src), sizeof(T) * (count), alignof(T))
// NOTE: [$ACAT] Dqn_ArenaCatalog ////////////////////////////////////////////////////////////////// // NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool); DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool);
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find (Dqn_ArenaCatalog *catalog, Dqn_Str8 label); DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find (Dqn_ArenaCatalog *catalog, Dqn_Str8 label);
DQN_API void Dqn_ArenaCatalog_AddLabelRef (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label); DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_ArenaCatalog_AddLabelCopy (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label); DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocFV (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelRef (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label); DQN_API bool Dqn_ArenaCatalog_Erase (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena);
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label);
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocFV (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...);

View File

@ -77,8 +77,8 @@ DQN_API void Dqn_CPU_SetFeature(Dqn_CPUReport *report, Dqn_CPUFeature feature)
DQN_API Dqn_CPUReport Dqn_CPU_Report() DQN_API Dqn_CPUReport Dqn_CPU_Report()
{ {
Dqn_CPUReport result = {}; Dqn_CPUReport result = {};
Dqn_CPUIDResult fn_0000_[16] = {}; Dqn_CPUIDResult fn_0000_[512] = {};
Dqn_CPUIDResult fn_8000_[64] = {}; Dqn_CPUIDResult fn_8000_[512] = {};
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000; int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax); int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
@ -102,8 +102,10 @@ DQN_API Dqn_CPUReport Dqn_CPU_Report()
// NOTE: Enumerate all CPUID results for the known function counts ///////////////////////////// // NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
{ {
DQN_ASSERT((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_)); DQN_ASSERTF((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_),
DQN_ASSERT((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_)); "Max standard count is %zu", STANDARD_FUNC_MAX_EAX + 1);
DQN_ASSERTF((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_),
"Max extended count is %zu", DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1);
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) { for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
Dqn_CPUIDArgs args = {}; Dqn_CPUIDArgs args = {};
@ -535,15 +537,15 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
// NOTE: Open log file for appending if requested ////////////////////////// // NOTE: Open log file for appending if requested //////////////////////////
Dqn_TicketMutex_Begin(&lib->log_file_mutex); Dqn_TicketMutex_Begin(&lib->log_file_mutex);
if (lib->log_to_file && !lib->log_file.handle && !lib->log_file.error) { if (lib->log_to_file && !lib->log_file.handle && !lib->log_file.error) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir)); Dqn_Str8 log_path = Dqn_OS_PathF(t_mem.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
lib->log_file = Dqn_OS_FileOpen(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly, nullptr); lib->log_file = Dqn_OS_FileOpen(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly, nullptr);
} }
Dqn_TicketMutex_End(&lib->log_file_mutex); Dqn_TicketMutex_End(&lib->log_file_mutex);
// NOTE: Generate the log header /////////////////////////////////////////// // NOTE: Generate the log header ///////////////////////////////////////////
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args); Dqn_Str8 log_line = Dqn_Log_MakeStr8(t_mem.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args);
// NOTE: Print log ///////////////////////////////////////////////////////// // NOTE: Print log /////////////////////////////////////////////////////////
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line); Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
@ -597,13 +599,14 @@ DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN
// NOTE: [$ERRS] Dqn_ErrorSink ///////////////////////////////////////////////////////////////////// // NOTE: [$ERRS] Dqn_ErrorSink /////////////////////////////////////////////////////////////////////
DQN_API Dqn_ErrorSink *Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode mode) DQN_API Dqn_ErrorSink *Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode mode)
{ {
Dqn_ThreadContext *thread_context = Dqn_ThreadContext_Get(); Dqn_TLS *tls = Dqn_TLS_Get();
Dqn_ErrorSink *result = &thread_context->error_sink; Dqn_ErrorSink *result = &tls->error_sink;
Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes); Dqn_usize arena_pos = Dqn_Arena_Pos(result->arena);
node->next = result->stack; Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes);
node->arena_pos = Dqn_Arena_Pos(result->arena); node->next = result->stack;
node->mode = mode; node->arena_pos = arena_pos;
result->stack = node; node->mode = mode;
result->stack = node;
return result; return result;
} }
@ -640,8 +643,8 @@ DQN_API void Dqn_ErrorSink_EndAndIgnore(Dqn_ErrorSink *error)
DQN_API bool Dqn_ErrorSink_EndAndLogError(Dqn_ErrorSink *error, Dqn_Str8 error_msg) DQN_API bool Dqn_ErrorSink_EndAndLogError(Dqn_ErrorSink *error, Dqn_Str8 error_msg)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(scratch.arena, error); Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(t_mem.arena, error);
if (node.error) { if (node.error) {
if (Dqn_Str8_HasData(error_msg)) { if (Dqn_Str8_HasData(error_msg)) {
Dqn_Log_TypeFCallSite(Dqn_LogType_Error, node.call_site, "%.*s: %.*s", DQN_STR_FMT(error_msg), DQN_STR_FMT(node.msg)); Dqn_Log_TypeFCallSite(Dqn_LogType_Error, node.call_site, "%.*s: %.*s", DQN_STR_FMT(error_msg), DQN_STR_FMT(node.msg));
@ -655,8 +658,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogError(Dqn_ErrorSink *error, Dqn_Str8 error_m
DQN_API bool Dqn_ErrorSink_EndAndLogErrorFV(Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args) DQN_API bool Dqn_ErrorSink_EndAndLogErrorFV(Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args);
bool result = Dqn_ErrorSink_EndAndLogError(error, log); bool result = Dqn_ErrorSink_EndAndLogError(error, log);
return result; return result;
} }
@ -665,8 +668,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogErrorF(Dqn_ErrorSink *error, DQN_FMT_ATTRIB
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args);
bool result = Dqn_ErrorSink_EndAndLogError(error, log); bool result = Dqn_ErrorSink_EndAndLogError(error, log);
va_end(args); va_end(args);
return result; return result;
@ -699,7 +702,7 @@ DQN_API void Dqn_ErrorSink_MakeFV_(Dqn_ErrorSink *error, uint32_t error_code, DQ
node->msg = Dqn_Str8_InitFV(error->arena, fmt, args); node->msg = Dqn_Str8_InitFV(error->arena, fmt, args);
node->error_code = error_code; node->error_code = error_code;
node->error = true; node->error = true;
node->call_site = Dqn_ThreadContext_Get()->call_site; node->call_site = Dqn_TLS_Get()->call_site;
if (node->mode == Dqn_ErrorSinkMode_ExitOnError) if (node->mode == Dqn_ErrorSinkMode_ExitOnError)
Dqn_ErrorSink_EndAndExitIfErrorF(error, error_code, "Fatal error %u", error_code); Dqn_ErrorSink_EndAndExitIfErrorF(error, error_code, "Fatal error %u", error_code);
} }

View File

@ -145,16 +145,16 @@
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE) #if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
#include <string.h> #include <string.h>
#if !defined(DQN_MEMCPY) #if !defined(DQN_MEMCPY)
#define DQN_MEMCPY(dest, src, count) memcpy(dest, src, count) #define DQN_MEMCPY(dest, src, count) memcpy((dest), (src), (count))
#endif #endif
#if !defined(DQN_MEMSET) #if !defined(DQN_MEMSET)
#define DQN_MEMSET(dest, value, count) memset(dest, value, count) #define DQN_MEMSET(dest, value, count) memset((dest), (value), (count))
#endif #endif
#if !defined(DQN_MEMCMP) #if !defined(DQN_MEMCMP)
#define DQN_MEMCMP(lhs, rhs, count) memcmp(lhs, rhs, count) #define DQN_MEMCMP(lhs, rhs, count) memcmp((lhs), (rhs), (count))
#endif #endif
#if !defined(DQN_MEMMOVE) #if !defined(DQN_MEMMOVE)
#define DQN_MEMMOVE(dest, src, count) memmove(dest, src, count) #define DQN_MEMMOVE(dest, src, count) memmove((dest), (src), (count))
#endif #endif
#endif #endif
@ -424,6 +424,9 @@ struct Dqn_ErrorSink
#define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value) #define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
#define Dqn_Atomic_SubU32(target, value) Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, (long)-value) #define Dqn_Atomic_SubU32(target, value) Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, (long)-value)
#define Dqn_Atomic_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value) #define Dqn_Atomic_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value)
#define Dqn_CountLeadingZerosU64(value) __lzcnt64(value)
#define Dqn_CPU_TSC() __rdtsc() #define Dqn_CPU_TSC() __rdtsc()
#define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence() #define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
#define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence() #define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
@ -439,6 +442,8 @@ struct Dqn_ErrorSink
#define Dqn_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) #define Dqn_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define Dqn_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL) #define Dqn_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define Dqn_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL) #define Dqn_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define Dqn_CountLeadingZerosU64(value) __builtin_clzll(value)
#if defined(DQN_COMPILER_GCC) #if defined(DQN_COMPILER_GCC)
#define Dqn_CPU_TSC() __rdtsc() #define Dqn_CPU_TSC() __rdtsc()
#else #else
@ -752,8 +757,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogErrorF
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorF (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API void Dqn_ErrorSink_EndAndExitIfErrorF (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorFV (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API void Dqn_ErrorSink_EndAndExitIfErrorFV (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
#define Dqn_ErrorSink_MakeFV(error, error_code, fmt, args) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args); } while (0) #define Dqn_ErrorSink_MakeFV(error, error_code, fmt, args) do { Dqn_TLS_SaveCallSite; Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args); } while (0)
#define Dqn_ErrorSink_MakeF(error, error_code, fmt, ...) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_ARGS__); } while (0) #define Dqn_ErrorSink_MakeF(error, error_code, fmt, ...) do { Dqn_TLS_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_ARGS__); } while (0)
DQN_API void Dqn_ErrorSink_MakeFV_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API void Dqn_ErrorSink_MakeFV_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_ErrorSink_MakeF_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API void Dqn_ErrorSink_MakeF_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...);

View File

@ -1,3 +1,4 @@
/*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ // $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
@ -12,6 +13,7 @@
// dqn_cgen.cpp // dqn_cgen.cpp
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
*/
Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_KEY_LIST[] = Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_KEY_LIST[] =
{ {
@ -48,6 +50,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] =
Dqn_CGenTableHeaderType_CppOpEquals, Dqn_CGenTableHeaderType_CppOpEquals,
Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySize,
Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_CppArraySizeField,
Dqn_CGenTableHeaderType_CppLabel,
Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenTypeInfo,
}; };
@ -57,6 +60,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST[] =
Dqn_CGenTableHeaderType_Table, Dqn_CGenTableHeaderType_Table,
Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppName,
Dqn_CGenTableHeaderType_CppValue, Dqn_CGenTableHeaderType_CppValue,
Dqn_CGenTableHeaderType_CppLabel,
Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenTypeInfo,
Dqn_CGenTableHeaderType_GenEnumCount, Dqn_CGenTableHeaderType_GenEnumCount,
}; };
@ -159,6 +163,7 @@ static bool Dqn_CGen_GatherTables_(Dqn_CGen *cgen, Dqn_ErrorSink *error)
// NOTE: Validate table headers //////////////////////////////////////////////////////////// // NOTE: Validate table headers ////////////////////////////////////////////////////////////
switch (table->type) { switch (table->type) {
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_Data: { case Dqn_CGenTableType_Data: {
} break; } break;
@ -299,7 +304,6 @@ DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV(int argc, char const **argv, Dqn_ErrorSi
MD_String8 file_name = MD_S8CString(DQN_CAST(char *)argv[arg_index]); MD_String8 file_name = MD_S8CString(DQN_CAST(char *)argv[arg_index]);
MD_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name); MD_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name);
for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) { for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) {
MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node);
has_error = true; has_error = true;
Dqn_CGen_LogF(message->kind, message->node, error, "%.*s", MD_S8VArg(message->string)); Dqn_CGen_LogF(message->kind, message->node, error, "%.*s", MD_S8VArg(message->string));
} }
@ -324,6 +328,7 @@ DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8(Dqn_CGenTableHeaderType type
case Dqn_CGenTableHeaderType_CppOpEquals: result = DQN_STR8("cpp_op_equals"); break; case Dqn_CGenTableHeaderType_CppOpEquals: result = DQN_STR8("cpp_op_equals"); break;
case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break; case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break;
case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break; case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break;
case Dqn_CGenTableHeaderType_CppLabel: result = DQN_STR8("cpp_label"); break;
case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break; case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break;
case Dqn_CGenTableHeaderType_GenEnumCount: result = DQN_STR8("gen_enum_count"); break; case Dqn_CGenTableHeaderType_GenEnumCount: result = DQN_STR8("gen_enum_count"); break;
case Dqn_CGenTableHeaderType_Count: result = DQN_STR8("XX BAD ENUM VALUE XX"); break; case Dqn_CGenTableHeaderType_Count: result = DQN_STR8("XX BAD ENUM VALUE XX"); break;
@ -345,56 +350,63 @@ DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit(MD_Node const *node,
if (result.enum_val == 0) { if (result.enum_val == 0) {
MD_CodeLoc loc = MD_CodeLocFromNode(DQN_CAST(MD_Node *)node); MD_CodeLoc loc = MD_CodeLocFromNode(DQN_CAST(MD_Node *)node);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_Str8 user_msg = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 user_msg = Dqn_Str8_InitFV(tmem.arena, fmt, args);
va_end(args); va_end(args);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = {};
builder.arena = scratch.arena; builder.arena = tmem.arena;
Dqn_Str8Builder_AppendF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DQN_STR_FMT(user_msg), MD_S8VArg(node->string)); Dqn_Str8Builder_AddF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DQN_STR_FMT(user_msg), MD_S8VArg(node->string));
for (Dqn_usize index = 0; index < valid_keys_size; index++) { for (Dqn_usize index = 0; index < valid_keys_size; index++) {
Dqn_CGenMapNodeToEnum const *validator = valid_keys + index; Dqn_CGenMapNodeToEnum const *validator = valid_keys + index;
Dqn_Str8Builder_AppendF(&builder, DQN_CAST(char *)"%s'%.*s'", index ? ", " : "", DQN_STR_FMT(validator->node_string)); Dqn_Str8Builder_AddF(&builder, DQN_CAST(char *)"%s'%.*s'", index ? ", " : "", DQN_STR_FMT(validator->node_string));
} }
Dqn_Str8 error_msg = Dqn_Str8Builder_Build(&builder, scratch.arena); Dqn_Str8 error_msg = Dqn_Str8Builder_Build(&builder, tmem.arena);
MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, DQN_CAST(char *) "%.*s", DQN_STR_FMT(error_msg)); MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, DQN_CAST(char *) "%.*s", DQN_STR_FMT(error_msg));
Dqn_OS_Exit(DQN_CAST(uint32_t)-1); Dqn_OS_Exit(DQN_CAST(uint32_t)-1);
} }
return result; return result;
} }
DQN_API Dqn_usize Dqn_CGen_NodeChildrenCount(MD_Node const *node)
{
Dqn_usize result = 0;
for (MD_EachNode(item, node->first_child))
result++;
return result;
}
DQN_API void Dqn_CGen_LogF(MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...) DQN_API void Dqn_CGen_LogF(MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...)
{ {
if (!error) if (!error)
return; return;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
builder.arena = scratch.arena;
MD_String8 kind_string = MD_StringFromMessageKind(kind); MD_String8 kind_string = MD_StringFromMessageKind(kind);
MD_CodeLoc loc = MD_CodeLocFromNode(node); MD_CodeLoc loc = MD_CodeLocFromNode(node);
Dqn_Str8Builder_AppendF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string)); Dqn_Str8Builder_AddF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string));
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_Str8Builder_AppendFV(&builder, fmt, args); Dqn_Str8Builder_AddFV(&builder, fmt, args);
va_end(args); va_end(args);
Dqn_Str8 msg = Dqn_Str8Builder_Build(&builder, scratch.arena); Dqn_Str8 msg = Dqn_Str8Builder_Build(&builder, tmem.arena);
Dqn_ErrorSink_MakeF(error, DQN_CAST(uint32_t)-1, "%.*s", DQN_STR_FMT(msg)); Dqn_ErrorSink_MakeF(error, DQN_CAST(uint32_t)-1, "%.*s", DQN_STR_FMT(msg));
} }
DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error) DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error)
{ {
bool result = true; bool result = true;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = {};
builder.arena = scratch.arena; builder.arena = tmem.arena;
for (Dqn_usize index = 0; index < header_count; index++) { for (Dqn_usize index = 0; index < header_count; index++) {
Dqn_Str8 header = headers[index]; Dqn_Str8 header = headers[index];
@ -402,12 +414,12 @@ DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const
MD_MapSlot *slot = MD_MapLookup(DQN_CAST(MD_Map *)&table->headers_map, MD_MapKeyStr(header_md)); MD_MapSlot *slot = MD_MapLookup(DQN_CAST(MD_Map *)&table->headers_map, MD_MapKeyStr(header_md));
if (!slot) { if (!slot) {
result = false; result = false;
Dqn_Str8Builder_AppendF(&builder, "%s%.*s", builder.count ? ", " : "", DQN_STR_FMT(header)); Dqn_Str8Builder_AddF(&builder, "%s%.*s", builder.count ? ", " : "", DQN_STR_FMT(header));
} }
} }
if (!result) { if (!result) {
Dqn_Str8 missing_headers = Dqn_Str8Builder_Build(&builder, scratch.arena); Dqn_Str8 missing_headers = Dqn_Str8Builder_Build(&builder, tmem.arena);
Dqn_CGen_LogF(MD_MessageKind_Error, Dqn_CGen_LogF(MD_MessageKind_Error,
table->headers_node, table->headers_node,
error, error,
@ -482,6 +494,26 @@ DQN_API bool Dqn_CGen_LookupNextTableInCodeGenTable(Dqn_CGen *cgen, Dqn_CGenTabl
return true; return true;
} }
DQN_API bool Dqn_CGen_WillCodeGenTypeName(Dqn_CGen const *cgen, Dqn_Str8 name)
{
if (!Dqn_Str8_HasData(name))
return false;
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
if (table->type != Dqn_CGenTableType_CodeGenStruct && table->type != Dqn_CGenTableType_CodeGenEnum)
continue;
for (Dqn_usize row_index = 0; row_index < table->row_count; row_index++) {
Dqn_CGenTableRow const *row = table->rows + row_index;
Dqn_CGenTableColumn const *column = row->columns + table->column_indexes[Dqn_CGenTableHeaderType_Name];
if (column->string == name)
return true;
}
}
return false;
}
static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile *cpp) static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile *cpp)
{ {
for (Dqn_CGenTableRowTag *tag = row->first_tag; tag; tag = tag->next) { for (Dqn_CGenTableRowTag *tag = row->first_tag; tag; tag = tag->next) {
@ -492,8 +524,8 @@ static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile
if (tag->comment.size <= 0) if (tag->comment.size <= 0)
break; break;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 prefix = Dqn_Str8_InitF(scratch.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment)); Dqn_Str8 prefix = Dqn_Str8_InitF(tmem.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment));
int line_padding = DQN_MAX(100 - (DQN_CAST(int) prefix.size + (Dqn_CppSpacePerIndent(cpp) * cpp->indent)), 0); int line_padding = DQN_MAX(100 - (DQN_CAST(int) prefix.size + (Dqn_CppSpacePerIndent(cpp) * cpp->indent)), 0);
Dqn_CppPrint(cpp, "%.*s", DQN_STR_FMT(prefix)); Dqn_CppPrint(cpp, "%.*s", DQN_STR_FMT(prefix));
for (int index = 0; index < line_padding; index++) for (int index = 0; index < line_padding; index++)
@ -508,62 +540,105 @@ static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile
} }
} }
Dqn_Str8 Dqn_CGen_StripQualifiersOnCppType_(Dqn_Arena *arena, Dqn_Str8 type)
{
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_Str8 result = Dqn_Str8_TrimWhitespaceAround(type);
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("*"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("constexpr"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("const"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("static"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8(" "), /*replace*/ DQN_STR8(""), /*start_index*/ 0, arena, Dqn_Str8EqCase_Sensitive);
result = Dqn_Str8_TrimWhitespaceAround(result);
return result;
}
DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_CppFile *cpp, Dqn_Str8 emit_prefix) DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_CppFile *cpp, Dqn_Str8 emit_prefix)
{ {
DQN_ASSERT(Dqn_Str8_HasData(emit_prefix));
if (emit & Dqn_CGenEmit_Prototypes) { if (emit & Dqn_CGenEmit_Prototypes) {
// NOTE: Generate type info enums ////////////////////////////////////////////////////////// // NOTE: Generate type info enums //////////////////////////////////////////////////////////
Dqn_CppEnumBlock(cpp, "%.*s_Type", DQN_STR_FMT(emit_prefix)) { Dqn_CppEnumBlock(cpp, "%.*sType", DQN_STR_FMT(emit_prefix)) {
Dqn_CppLine(cpp, "%.*s_Type_Nil,", DQN_STR_FMT(emit_prefix)); Dqn_CppLine(cpp, "%.*sType_Nil,", DQN_STR_FMT(emit_prefix));
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);)
Dqn_CppLine(cpp, "%.*s_Type_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)); Dqn_CppLine(cpp, "%.*sType_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string));
} }
Dqn_CppLine(cpp, "%.*s_Type_Count,", DQN_STR_FMT(emit_prefix)); Dqn_CppLine(cpp, "%.*sType_Count,", DQN_STR_FMT(emit_prefix));
} }
Dqn_CppNewLine(cpp);
// NOTE: Generate structs + enums ////////////////////////////////////////////////////////////// // NOTE: Generate structs + enums //////////////////////////////////////////////////////////////
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
switch (table->type) { switch (table->type) {
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_Data: continue; case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_CodeGenBuiltinTypes: continue;
case Dqn_CGenTableType_Data: continue;
case Dqn_CGenTableType_CodeGenStruct: { case Dqn_CGenTableType_CodeGenStruct: {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
// TODO(doyle): Verify the codegen table has the headers from the table it references // TODO(doyle): Verify the codegen table has the headers from the table it references
Dqn_CppStructBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) { int longest_type_name = 0;
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
Dqn_usize length = cpp_type.column.string.size;
Dqn_Str8 find_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
if (Dqn_CGen_WillCodeGenTypeName(cgen, find_name))
length += emit_prefix.size;
longest_type_name = DQN_MAX(longest_type_name, DQN_CAST(int)length);
}
Dqn_CppStructBlock(cpp, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row); Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row); Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row);
int name_to_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - DQN_CAST(int) cpp_type.column.string.size;
if (cpp_name.column.string.size <= 0 || cpp_type.column.string.size <= 0) if (cpp_name.column.string.size <= 0 || cpp_type.column.string.size <= 0)
continue; continue;
Dqn_CGen_EmitRowWhitespace_(row, cpp);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); // NOTE: Generate cpp array size ///////////////////////////////////
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 array_size = {}; Dqn_Str8 array_size = {};
if (cpp_array_size.column.string.size) if (cpp_array_size.column.string.size)
array_size = Dqn_Str8_InitF(scratch.arena, "[%.*s]", DQN_STR_FMT(cpp_array_size.column.string)); array_size = Dqn_Str8_InitF(tmem.arena, "[%.*s]", DQN_STR_FMT(cpp_array_size.column.string));
// NOTE: Check if we're referencing a code generated type. If we
// are, append the `emit_prefix`
Dqn_Str8 emit_cpp_type = cpp_type.column.string;
{
Dqn_Str8 find_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, emit_cpp_type);
if (Dqn_CGen_WillCodeGenTypeName(cgen, find_name))
emit_cpp_type = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type.column.string));
}
int name_to_type_padding = 1 + longest_type_name - DQN_CAST(int) emit_cpp_type.size;
// NOTE: Emit decl /////////////////////////////////////////////////
Dqn_CGen_EmitRowWhitespace_(row, cpp);
Dqn_CppLine(cpp, Dqn_CppLine(cpp,
"%.*s%*s%.*s%.*s;", "%.*s%*s%.*s%.*s;",
DQN_STR_FMT(cpp_type.column.string), DQN_STR_FMT(emit_cpp_type),
name_to_type_padding, name_to_type_padding,
"", "",
DQN_STR_FMT(cpp_name.column.string), DQN_STR_FMT(cpp_name.column.string),
DQN_STR_FMT(array_size)); DQN_STR_FMT(array_size));
} }
} }
Dqn_CppNewLine(cpp);
Dqn_CppNewLine(cpp);
} }
} break; } break;
case Dqn_CGenTableType_CodeGenEnum: { case Dqn_CGenTableType_CodeGenEnum: {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
Dqn_CppEnumBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) { Dqn_CppEnumBlock(cpp, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
Dqn_usize enum_count = 0; Dqn_usize enum_count = 0;
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
@ -576,13 +651,15 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
Dqn_CGen_EmitRowWhitespace_(row, cpp); Dqn_CGen_EmitRowWhitespace_(row, cpp);
if (cpp_value.column.string.size) { if (cpp_value.column.string.size) {
Dqn_CppLine(cpp, Dqn_CppLine(cpp,
"%.*s_%.*s = %.*s,", "%.*s%.*s_%.*s = %.*s,",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
DQN_STR_FMT(cpp_name.column.string), DQN_STR_FMT(cpp_name.column.string),
DQN_STR_FMT(cpp_value.column.string)); DQN_STR_FMT(cpp_value.column.string));
} else { } else {
Dqn_CppLine(cpp, Dqn_CppLine(cpp,
"%.*s_%.*s = %zu,", "%.*s%.*s_%.*s = %zu,",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
DQN_STR_FMT(cpp_name.column.string), DQN_STR_FMT(cpp_name.column.string),
row_index); row_index);
@ -593,11 +670,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
Dqn_CGenTableColumn gen_enum_count_column = it.cgen_table_column[Dqn_CGenTableHeaderType_GenEnumCount]; Dqn_CGenTableColumn gen_enum_count_column = it.cgen_table_column[Dqn_CGenTableHeaderType_GenEnumCount];
if (gen_enum_count_column.string.size) if (gen_enum_count_column.string.size)
Dqn_CppLine(cpp, Dqn_CppLine(cpp,
"%.*s_%.*s = %zu,", "%.*s%.*s_%.*s = %zu,",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
DQN_STR_FMT(gen_enum_count_column.string), DQN_STR_FMT(gen_enum_count_column.string),
enum_count); enum_count);
} }
Dqn_CppNewLine(cpp);
} }
} break; } break;
} }
@ -606,24 +685,27 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
// NOTE: Generate enums for struct fields ////////////////////////////////////////////////// // NOTE: Generate enums for struct fields //////////////////////////////////////////////////
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
switch (table->type) { switch (table->type) {
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_Data: continue; case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
case Dqn_CGenTableType_CodeGenEnum: continue; case Dqn_CGenTableType_Data: continue;
case Dqn_CGenTableType_CodeGenBuiltinTypes: continue;
case Dqn_CGenTableType_CodeGenEnum: continue;
case Dqn_CGenTableType_CodeGenStruct: { case Dqn_CGenTableType_CodeGenStruct: {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
Dqn_Str8 struct_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 struct_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppEnumBlock(cpp, "%.*sTypeField", DQN_STR_FMT(struct_name)) { Dqn_CppEnumBlock(cpp, "%.*s%.*sTypeField", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name)) {
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
if (cpp_name.column.string.size <= 0) if (cpp_name.column.string.size <= 0)
continue; continue;
Dqn_CGen_EmitRowWhitespace_(row, cpp); Dqn_CGen_EmitRowWhitespace_(row, cpp);
Dqn_CppLine(cpp, "%.*sTypeField_%.*s,", DQN_STR_FMT(struct_name), DQN_STR_FMT(cpp_name.column.string)); Dqn_CppLine(cpp, "%.*s%.*sTypeField_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name), DQN_STR_FMT(cpp_name.column.string));
} }
Dqn_CppLine(cpp, "%.*sTypeField_Count,", DQN_STR_FMT(struct_name)); Dqn_CppLine(cpp, "%.*s%.*sTypeField_Count,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name));
} }
Dqn_CppNewLine(cpp);
} }
} break; } break;
} }
@ -636,10 +718,11 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) { Dqn_CppStructBlock(cpp, "%.*s%.*sStr8ToEnumResult", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
Dqn_CppLine(cpp, "bool success;"); Dqn_CppLine(cpp, "bool success;");
Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name)); Dqn_CppLine(cpp, "%.*s%.*s value;", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
} }
Dqn_CppNewLine(cpp);
} }
} }
@ -649,7 +732,12 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); Dqn_CppLine(cpp,
"%.*s%.*sStr8ToEnumResult %.*s%.*s_Str8ToEnum(Dqn_Str8 string);",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name),
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name));
} }
} }
@ -664,8 +752,18 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
continue; continue;
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppLine(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); Dqn_CppLine(cpp,
Dqn_CppLine(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); "bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs);",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name),
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name));
Dqn_CppLine(cpp,
"bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs);",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name),
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(type_name));
} }
} }
} }
@ -679,8 +777,29 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
continue; continue;
Dqn_Str8 struct_or_enum_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 struct_or_enum_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeField const g_%.*s_type_fields[] =", DQN_STR_FMT(struct_or_enum_name)) { Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeField const g_%.*s%.*s_type_fields[] =", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name)) {
if (table->type == Dqn_CGenTableType_CodeGenStruct) { if (table->type == Dqn_CGenTableType_CodeGenStruct) {
// NOTE: Construct the cpp type string first. We will prepend `emit_prefix`
// for types that are declared in the same mdesk file. We will also
// calculate the longest type name that we will generate for whitespace
// padding purposes.
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
Dqn_usize longest_cpp_type_name = 0;
auto cpp_type_list = Dqn_SArray_Init<Dqn_Str8>(tmem.arena, it.table->row_count, Dqn_ZeroMem_Yes);
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
Dqn_Str8 cpp_type_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
if (Dqn_CGen_WillCodeGenTypeName(cgen, cpp_type_name))
cpp_type_name = Dqn_Str8_InitF_TLS("%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type_name));
longest_cpp_type_name = DQN_MAX(longest_cpp_type_name, cpp_type_name.size);
Dqn_SArray_Add(&cpp_type_list, cpp_type_name);
}
// NOTE: Iterate each row and emit the C++ declarations ////////////////////
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
@ -688,12 +807,12 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row); Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row);
Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row); Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row);
Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row); Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row);
Dqn_CGenLookupColumnAtHeader cpp_label = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppLabel].string, row);
bool cpp_is_ptr_b32 = cpp_is_ptr.column.string == DQN_STR8("true"); bool cpp_is_ptr_b32 = cpp_is_ptr.column.string == DQN_STR8("true");
Dqn_Str8 cpp_array_size_str8 = Dqn_Str8_HasData(cpp_array_size.column.string) ? cpp_array_size.column.string : DQN_STR8("0"); Dqn_Str8 cpp_array_size_str8 = Dqn_Str8_HasData(cpp_array_size.column.string) ? cpp_array_size.column.string : DQN_STR8("0");
Dqn_CGenTableColumn struct_name = it.cgen_table_row->columns[table->column_indexes[Dqn_CGenTableHeaderType_Name]]; Dqn_CGenTableColumn struct_name = it.cgen_table_row->columns[table->column_indexes[Dqn_CGenTableHeaderType_Name]];
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 cpp_array_size_field_str8 = DQN_STR8("NULL"); Dqn_Str8 cpp_array_size_field_str8 = DQN_STR8("NULL");
if (cpp_array_size_field.column.string.size) { if (cpp_array_size_field.column.string.size) {
@ -707,57 +826,100 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
if (sub_cpp_name.string == cpp_array_size_field.column.string) if (sub_cpp_name.string == cpp_array_size_field.column.string)
index_the_field_references = sub_row_index; index_the_field_references = sub_row_index;
} }
cpp_array_size_field_str8 = Dqn_Str8_InitF(scratch.arena, cpp_array_size_field_str8 =
"&g_%.*s_type_fields[%zu]", Dqn_Str8_InitF_TLS("&g_%.*s%.*s_type_fields[%zu]",
DQN_STR_FMT(struct_name.string), DQN_STR_FMT(emit_prefix),
index_the_field_references); DQN_STR_FMT(struct_name.string),
index_the_field_references);
} }
Dqn_Str8 orig_cpp_type_info = Dqn_Str8_InitF(scratch.arena, "%.*s", DQN_STR_FMT(cpp_type.column.string)); Dqn_Str8 cpp_type_name = cpp_type_list.data[row_index];
Dqn_Str8 cpp_type_info = orig_cpp_type_info; Dqn_Str8 orig_cpp_type = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("*"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("constexpr"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("const"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("static"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8(" "), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF(scratch.arena, "%.*s_Type_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type_info)); Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; Dqn_usize cpp_type_padding = 1 + longest_cpp_type_name - cpp_type_name.size;
Dqn_usize cpp_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - cpp_type.column.string.size;
Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size);
Dqn_CppLine(cpp, Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(orig_cpp_type));
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*salignof(%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},", Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type.size - cpp_type_name.size);
row_index,
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "",
// NOTE: offsetof(a, b) Dqn_Str8 cpp_label_str8 = cpp_name.column.string;
DQN_STR_FMT(struct_or_enum_name), Dqn_usize cpp_label_str8_padding = cpp_name_padding;
DQN_STR_FMT(cpp_name.column.string), if (cpp_label.column.string.size) {
cpp_name_padding, "", cpp_label_str8 = cpp_label.column.string;
cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size;
}
// NOTE: sizeof(a->b) Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
DQN_STR_FMT(struct_or_enum_name),
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "",
// NOTE: alignof(a->b) // NOTE: row
DQN_STR_FMT(cpp_type.column.string), Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index);
cpp_type_padding, "",
// NOTE: Type string // NOTE: name
DQN_STR_FMT(cpp_type.column.string), Dqn_Str8Builder_AddF(&builder,
cpp_type_padding, "", "DQN_STR8(\"%.*s\"),%*s",
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "");
// NOTE: Type as enum // NOTE: label
DQN_STR_FMT(cpp_type_enum), Dqn_Str8Builder_AddF(&builder,
cpp_type_enum_padding, "", "DQN_STR8(\"%.*s\"),%*s",
DQN_STR_FMT(cpp_label_str8),
cpp_label_str8_padding, "");
cpp_is_ptr_b32 ? "true" : "false", // NOTE: value
cpp_is_ptr_b32 ? " " : "", Dqn_Str8Builder_AddF(&builder,
DQN_STR_FMT(cpp_array_size_str8), "/*value*/ 0, ");
DQN_STR_FMT(cpp_array_size_field_str8));
// NOTE: offsetof(a, b)
Dqn_Str8Builder_AddF(&builder,
"offsetof(%.*s%.*s, %.*s),%*s",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(struct_or_enum_name),
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "");
// NOTE: sizeof(a->b)
Dqn_Str8Builder_AddF(&builder,
"sizeof(((%.*s%.*s*)0)->%.*s),%*s",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(struct_or_enum_name),
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "");
// NOTE: alignof(a)
if (cpp_type_name == DQN_STR8("void")) {
Dqn_Str8 proxy_type = DQN_STR8("char");
Dqn_usize proxy_type_padding = 1 + longest_cpp_type_name - proxy_type.size;
Dqn_Str8Builder_AddF(&builder, "alignof(%.*s),%*s", DQN_STR_FMT(proxy_type), proxy_type_padding, "");
} else {
Dqn_Str8Builder_AddF(&builder,
"alignof(%.*s),%*s",
DQN_STR_FMT(cpp_type_name),
cpp_type_padding, "");
}
// NOTE: Type string
Dqn_Str8Builder_AddF(&builder,
"DQN_STR8(\"%.*s\"),%*s",
DQN_STR_FMT(cpp_type_name),
cpp_type_padding, "");
// NOTE: Type as enum
Dqn_Str8Builder_AddF(&builder,
"%.*s,%*s",
DQN_STR_FMT(cpp_type_enum),
cpp_type_enum_padding, "");
Dqn_Str8Builder_AddF(&builder,
"/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},",
cpp_is_ptr_b32 ? "true" : "false",
cpp_is_ptr_b32 ? " " : "",
DQN_STR_FMT(cpp_array_size_str8),
DQN_STR_FMT(cpp_array_size_field_str8));
Dqn_Str8 line = Dqn_Str8Builder_Build(&builder, tmem.arena);
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
} }
} else { } else {
DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenEnum); DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenEnum);
@ -765,32 +927,68 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
Dqn_CGenLookupColumnAtHeader cpp_value = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppValue].string, row); Dqn_CGenLookupColumnAtHeader cpp_value = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppValue].string, row);
Dqn_CGenLookupColumnAtHeader cpp_label = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppLabel].string, row);
if (cpp_name.column.string.size <= 0) if (cpp_name.column.string.size <= 0)
continue; continue;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index); Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF_TLS("%zu", row_index);
Dqn_CppLine(cpp, Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name));
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), alignof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},",
row_index,
// NOTE: Name string Dqn_Str8 cpp_label_str8 = cpp_name.column.string;
DQN_STR_FMT(cpp_name.column.string), Dqn_usize cpp_label_str8_padding = cpp_name_padding;
cpp_name_padding, "", if (cpp_label.column.string.size) {
cpp_label_str8 = cpp_label.column.string;
cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size;
}
// NOTE: Value Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
DQN_STR_FMT(cpp_value_str8), // NOTE: row
Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index);
// NOTE: sizeof(a) // NOTE: name
DQN_STR_FMT(struct_or_enum_name), Dqn_Str8Builder_AddF(&builder,
"DQN_STR8(\"%.*s\"),%*s",
DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "");
// NOTE: alignof(a) // NOTE: label
DQN_STR_FMT(struct_or_enum_name), Dqn_Str8Builder_AddF(&builder,
"DQN_STR8(\"%.*s\"),%*s",
DQN_STR_FMT(cpp_label_str8),
cpp_label_str8_padding, "");
// NOTE: ..._Type_... // NOTE: value
DQN_STR_FMT(emit_prefix), Dqn_Str8Builder_AddF(&builder, "/*value*/ %.*s, ", DQN_STR_FMT(cpp_value_str8));
DQN_STR_FMT(struct_or_enum_name));
// NOTE: offsetof(a, b)
Dqn_Str8Builder_AddF(&builder, "/*offsetof*/ 0, ");
// NOTE: sizeof(a->b)
Dqn_Str8Builder_AddF(&builder,
"sizeof(%.*s%.*s), ",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(struct_or_enum_name));
// NOTE: alignof(a->b)
Dqn_Str8Builder_AddF(&builder,
"alignof(%.*s%.*s), ",
DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(struct_or_enum_name));
// TODO: Type string
Dqn_Str8Builder_AddF(&builder, "DQN_STR8(\"\"), ");
// NOTE: Type as enum
Dqn_Str8Builder_AddF(&builder, "%.*s, ", DQN_STR_FMT(cpp_type_enum));
Dqn_Str8Builder_AddF(&builder, "/*is_pointer*/ false, ");
Dqn_Str8Builder_AddF(&builder, "/*array_size*/ 0, ");
Dqn_Str8Builder_AddF(&builder, "/*array_size_field*/ NULL},");
Dqn_Str8 line = Dqn_Str8Builder_Build_TLS(&builder);
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
} }
} }
} }
@ -800,18 +998,38 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
int longest_name_across_all_tables = 0; int longest_name_across_all_tables = 0;
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
longest_name_across_all_tables = DQN_MAX(longest_name_across_all_tables, DQN_CAST(int)it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string.size);
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
type_name = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
longest_name_across_all_tables = DQN_MAX(longest_name_across_all_tables, DQN_CAST(int)type_name.size);
} }
} }
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeInfo const g_%.*s_types[] =", DQN_STR_FMT(emit_prefix)) { Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeInfo const g_%.*stypes[] =", DQN_STR_FMT(emit_prefix)) {
Dqn_CppLine(cpp, "{DQN_STR8(\"\"),%*sDqn_TypeKind_Nil, 0, /*fields*/ NULL, /*count*/ 0},", 1 + longest_name_across_all_tables, ""); Dqn_CppLine(cpp, "{DQN_STR8(\"\"),%*sDqn_TypeKind_Nil, 0, /*fields*/ NULL, /*count*/ 0},", 1 + longest_name_across_all_tables, "");
Dqn_usize longest_type_name = 0;
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.size; if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
type_name = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
longest_type_name = DQN_MAX(longest_type_name, type_name.size);
}
}
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
type_name = Dqn_Str8_InitF_TLS("%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.size;
Dqn_Str8 type_info_kind = {}; Dqn_Str8 type_info_kind = {};
char const *type_info_kind_padding = ""; char const *type_info_kind_padding = "";
if (table->type == Dqn_CGenTableType_CodeGenEnum) { if (table->type == Dqn_CGenTableType_CodeGenEnum) {
@ -836,27 +1054,56 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
fields_count_int += Dqn_Str8_HasData(cpp_name.column.string); fields_count_int += Dqn_Str8_HasData(cpp_name.column.string);
} }
fields_count = Dqn_Str8_InitF(scratch.arena, "%d", fields_count_int); fields_count = Dqn_Str8_InitF_TLS("%d", fields_count_int);
} }
Dqn_Str8 fields = DQN_STR8("NULL"); Dqn_Str8 fields = DQN_STR8("NULL");
int fields_padding = 1; int fields_padding = 1;
if (table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) { if (table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) {
fields_padding = name_padding; fields_padding = name_padding;
fields = Dqn_Str8_InitF(scratch.arena, "g_%.*s_type_fields", DQN_STR_FMT(type_name)); fields = Dqn_Str8_InitF(tmem.arena, "g_%.*s_type_fields", DQN_STR_FMT(type_name));
} }
Dqn_CppLine(cpp, Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
"{DQN_STR8(\"%.*s\"),%*s%.*s, %ssizeof(%.*s),%*s/*fields*/ %.*s,%*s/*count*/ %.*s},",
DQN_STR_FMT(type_name), // NOTE: name
name_padding, "", Dqn_Str8Builder_AddF(&builder,
DQN_STR_FMT(type_info_kind), "{DQN_STR8(\"%.*s\"),%*s",
type_info_kind_padding, DQN_STR_FMT(type_name),
DQN_STR_FMT(type_name), name_padding,
name_padding, "", "");
DQN_STR_FMT(fields),
fields_padding, "", // NOTE: Dqn_TypeKind_{Nil|Basic|Enum|Struct}
DQN_STR_FMT(fields_count)); Dqn_Str8Builder_AddF(&builder,
"%.*s,%s",
DQN_STR_FMT(type_info_kind),
type_info_kind_padding);
// NOTE: sizeof(T)
if (type_name == DQN_STR8("void")) {
Dqn_Str8Builder_AddF(&builder, "0,%*s", name_padding, "");
} else {
Dqn_Str8Builder_AddF(&builder,
"sizeof(%.*s),%*s",
DQN_STR_FMT(type_name),
name_padding,
"");
}
// NOTE: Pointer to Dqn_TypeField[]
Dqn_Str8Builder_AddF(&builder,
"/*fields*/ %.*s,%*s",
DQN_STR_FMT(fields),
fields_padding,
"");
// NOTE: Dqn_TypeField length
Dqn_Str8Builder_AddF(&builder,
"/*count*/ %.*s},",
DQN_STR_FMT(fields_count));
Dqn_Str8 line = Dqn_Str8Builder_Build_TLS(&builder);
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
} }
} }
} }
@ -868,19 +1115,21 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppFuncBlock(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(type_name)); Dqn_CppFuncBlock(cpp, "%.*s%.*sStr8ToEnumResult %.*s%.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s_type_fields); index++", DQN_STR_FMT(type_name)) { Dqn_CppLine(cpp, "%.*s%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s%.*s_type_fields); index++", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
Dqn_CppIfChain(cpp) { Dqn_CppIfChain(cpp) {
Dqn_CppLine(cpp, "Dqn_TypeField field = g_%.*s_type_fields[index];", DQN_STR_FMT(type_name)); Dqn_CppLine(cpp, "Dqn_TypeField field = g_%.*s%.*s_type_fields[index];", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
Dqn_CppIfOrElseIfBlock(cpp, "string == field.name") { Dqn_CppIfOrElseIfBlock(cpp, "string == field.name") {
Dqn_CppLine(cpp, "result.success = true;"); Dqn_CppLine(cpp, "result.success = true;");
Dqn_CppLine(cpp, "result.value = (%.*s)index;", DQN_STR_FMT(type_name)); Dqn_CppLine(cpp, "result.value = (%.*s%.*s)index;", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
} }
} }
} }
Dqn_CppLine(cpp, "return result;"); Dqn_CppLine(cpp, "return result;");
} }
Dqn_CppNewLine(cpp);
} }
} }
@ -895,7 +1144,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
continue; continue;
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
Dqn_CppFuncBlock(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) { Dqn_CppFuncBlock(cpp, "bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
Dqn_CGenTableRow const *row = it.table->rows + row_index; Dqn_CGenTableRow const *row = it.table->rows + row_index;
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
@ -955,11 +1204,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
} }
Dqn_CppLine(cpp, "return true;"); Dqn_CppLine(cpp, "return true;");
} }
Dqn_CppNewLine(cpp);
Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) { Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
Dqn_CppLine(cpp, "bool result = !(lhs == rhs);"); Dqn_CppLine(cpp, "bool result = !(lhs == rhs);");
Dqn_CppLine(cpp, "return result;"); Dqn_CppLine(cpp, "return result;");
} }
Dqn_CppNewLine(cpp);
} }
} }
} }

View File

@ -1,6 +1,7 @@
#if !defined(DQN_CGEN_H) #if !defined(DQN_CGEN_H)
#define DQN_CGEN_H #define DQN_CGEN_H
/*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ // $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
@ -15,6 +16,7 @@
// dqn_cgen.h -- C/C++ code generation from table data in Metadesk files // dqn_cgen.h -- C/C++ code generation from table data in Metadesk files
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
*/
// NOTE: [$CGEN] Dqn_CGen ////////////////////////////////////////////////////////////////////////// // NOTE: [$CGEN] Dqn_CGen //////////////////////////////////////////////////////////////////////////
#if !defined(MD_H) #if !defined(MD_H)
@ -70,6 +72,7 @@ enum Dqn_CGenTableHeaderType
Dqn_CGenTableHeaderType_CppOpEquals, Dqn_CGenTableHeaderType_CppOpEquals,
Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySize,
Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_CppArraySizeField,
Dqn_CGenTableHeaderType_CppLabel,
Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenTypeInfo,
Dqn_CGenTableHeaderType_GenEnumCount, Dqn_CGenTableHeaderType_GenEnumCount,
Dqn_CGenTableHeaderType_Count, Dqn_CGenTableHeaderType_Count,
@ -162,6 +165,7 @@ enum Dqn_CGenEmit
DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV (int argc, char const **argv, Dqn_ErrorSink *error); DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV (int argc, char const **argv, Dqn_ErrorSink *error);
DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8 (Dqn_CGenTableHeaderType type); DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8 (Dqn_CGenTableHeaderType type);
DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit (MD_Node const *node, Dqn_CGenMapNodeToEnum const *valid_keys, Dqn_usize valid_keys_size, char const *fmt, ...); DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit (MD_Node const *node, Dqn_CGenMapNodeToEnum const *valid_keys, Dqn_usize valid_keys_size, char const *fmt, ...);
DQN_API Dqn_usize Dqn_CGen_NodeChildrenCount (MD_Node const *node);
DQN_API void Dqn_CGen_LogF (MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...); DQN_API void Dqn_CGen_LogF (MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...);
DQN_API bool Dqn_CGen_TableHasHeaders (Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error); DQN_API bool Dqn_CGen_TableHasHeaders (Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error);
DQN_API Dqn_CGenLookupColumnAtHeader Dqn_CGen_LookupColumnAtHeader (Dqn_CGenTable *table, Dqn_Str8 header, Dqn_CGenTableRow const *row); DQN_API Dqn_CGenLookupColumnAtHeader Dqn_CGen_LookupColumnAtHeader (Dqn_CGenTable *table, Dqn_Str8 header, Dqn_CGenTableRow const *row);

View File

@ -56,14 +56,52 @@ DQN_API Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<
return result; return result;
} }
DQN_API Dqn_Str16 Dqn_Slice_Str16Render(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array, Dqn_Str16 separator)
{
Dqn_Str16 result = {};
if (!arena)
return result;
Dqn_usize total_size = 0;
for (Dqn_usize index = 0; index < array.size; index++) {
if (index)
total_size += separator.size;
Dqn_Str16 item = array.data[index];
total_size += item.size;
}
result = {Dqn_Arena_NewArray(arena, wchar_t, total_size + 1, Dqn_ZeroMem_No), total_size};
if (result.data) {
Dqn_usize write_index = 0;
for (Dqn_usize index = 0; index < array.size; index++) {
if (index) {
DQN_MEMCPY(result.data + write_index, separator.data, separator.size * sizeof(result.data[0]));
write_index += separator.size;
}
Dqn_Str16 item = array.data[index];
DQN_MEMCPY(result.data + write_index, item.data, item.size * sizeof(result.data[0]));
write_index += item.size;
}
}
result.data[total_size] = 0;
return result;
}
DQN_API Dqn_Str16 Dqn_Slice_Str16RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array)
{
Dqn_Str16 result = Dqn_Slice_Str16Render(arena, array, DQN_STR16(L" "));
return result;
}
#if !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_DSMAP)
// NOTE: [$DMAP] Dqn_DSMap ///////////////////////////////////////////////////////////////////////// // NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64) DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
{ {
Dqn_DSMapKey result = {}; Dqn_DSMapKey result = {};
result.type = Dqn_DSMapKeyType_U64NoHash; result.type = Dqn_DSMapKeyType_U64NoHash;
result.payload.u64 = u64; result.u64 = u64;
result.hash = DQN_CAST(uint32_t)u64; result.hash = DQN_CAST(uint32_t) u64;
return result; return result;
} }
@ -72,11 +110,15 @@ DQN_API bool Dqn_DSMap_KeyEquals(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs)
bool result = false; bool result = false;
if (lhs.type == rhs.type && lhs.hash == rhs.hash) { if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
switch (lhs.type) { switch (lhs.type) {
case Dqn_DSMapKeyType_Invalid: result = true; break; case Dqn_DSMapKeyType_Invalid: result = true; break;
case Dqn_DSMapKeyType_U64NoHash: result = true; break; case Dqn_DSMapKeyType_U64NoHash: result = true; break;
case Dqn_DSMapKeyType_U64: result = lhs.payload.u64 == rhs.payload.u64; break; case Dqn_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break;
case Dqn_DSMapKeyType_Buffer: result = lhs.payload.buffer.size == rhs.payload.buffer.size &&
memcmp(lhs.payload.buffer.data, rhs.payload.buffer.data, lhs.payload.buffer.size) == 0; break; case Dqn_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
case Dqn_DSMapKeyType_Buffer: {
if (lhs.buffer_size == rhs.buffer_size)
result = DQN_MEMCMP(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0;
} break;
} }
} }
return result; return result;

View File

@ -110,23 +110,22 @@ template <typename T> using Dqn_FArray64 = Dqn_FArray<T, 64>;
// NOTE: [$DMAP] Dqn_DSMap ///////////////////////////////////////////////////////////////////////// // NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
enum Dqn_DSMapKeyType enum Dqn_DSMapKeyType
{ {
// Key | Key Hash | Map Index
Dqn_DSMapKeyType_Invalid, Dqn_DSMapKeyType_Invalid,
Dqn_DSMapKeyType_U64, // Use a U64 key that is `hash(u64) % size` to map into the table Dqn_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size
Dqn_DSMapKeyType_U64NoHash, // Use a U64 key that is `u64 % size` to map into the table Dqn_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size
Dqn_DSMapKeyType_Buffer, // Use a buffer key that is `hash(buffer) % size` to map into the table Dqn_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size
Dqn_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size
}; };
struct Dqn_DSMapKey struct Dqn_DSMapKey
{ {
Dqn_DSMapKeyType type; Dqn_DSMapKeyType type;
uint32_t hash; uint32_t hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches
union Payload { void const *buffer_data;
struct Buffer { uint32_t buffer_size;
void const *data; uint64_t u64;
uint32_t size; bool no_copy_buffer;
} buffer;
uint64_t u64;
} payload;
}; };
template <typename T> template <typename T>
@ -136,6 +135,13 @@ struct Dqn_DSMapSlot
T value; ///< Hash table value T value; ///< Hash table value
}; };
typedef uint32_t Dqn_DSMapFlags;
enum Dqn_DSMapFlags_
{
Dqn_DSMapFlags_Nil = 0,
Dqn_DSMapFlags_DontFreeArenaOnResize = 1 << 0,
};
using Dqn_DSMapHashFunction = uint32_t(Dqn_DSMapKey key, uint32_t seed); using Dqn_DSMapHashFunction = uint32_t(Dqn_DSMapKey key, uint32_t seed);
template <typename T> struct Dqn_DSMap template <typename T> struct Dqn_DSMap
{ {
@ -147,6 +153,7 @@ template <typename T> struct Dqn_DSMap
uint32_t initial_size; // Initial map size, map cannot shrink on erase below this size uint32_t initial_size; // Initial map size, map cannot shrink on erase below this size
Dqn_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set Dqn_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set
uint32_t hash_seed; // Seed for the hashing function, when 0, DQN_DS_MAP_DEFAULT_HASH_SEED is used uint32_t hash_seed; // Seed for the hashing function, when 0, DQN_DS_MAP_DEFAULT_HASH_SEED is used
Dqn_DSMapFlags flags;
}; };
template <typename T> struct Dqn_DSMapResult template <typename T> struct Dqn_DSMapResult
@ -179,7 +186,6 @@ template <typename T> struct Dqn_ListIterator
template <typename T> struct Dqn_List template <typename T> struct Dqn_List
{ {
Dqn_Arena *arena;
Dqn_usize count; // Cumulative count of all items made across all list chunks Dqn_usize count; // Cumulative count of all items made across all list chunks
Dqn_usize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node. Dqn_usize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node.
Dqn_ListChunk<T> *head; Dqn_ListChunk<T> *head;
@ -254,6 +260,7 @@ template <typename T> void Dqn_SArra
#endif // !defined(DQN_NO_SARRAY) #endif // !defined(DQN_NO_SARRAY)
#if !defined(DQN_NO_FARRAY) #if !defined(DQN_NO_FARRAY)
template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_Init (T const *array, Dqn_usize count); template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_Init (T const *array, Dqn_usize count);
#define Dqn_FArray_HasData(array) ((array).data && (array).size)
template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_InitSlice (Dqn_Slice<T> slice); template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_InitSlice (Dqn_Slice<T> slice);
template <typename T, Dqn_usize N, Dqn_usize K> Dqn_FArray<T, N> Dqn_FArray_InitCArray (T const (&items)[K]); template <typename T, Dqn_usize N, Dqn_usize K> Dqn_FArray<T, N> Dqn_FArray_InitCArray (T const (&items)[K]);
template <typename T, Dqn_usize N> bool Dqn_FArray_IsValid (Dqn_FArray<T, N> const *array); template <typename T, Dqn_usize N> bool Dqn_FArray_IsValid (Dqn_FArray<T, N> const *array);
@ -289,9 +296,11 @@ template <typename T> Dqn_Slice<T> Dqn_Slice
template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator); Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator);
Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array); Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array);
Dqn_Str16 Dqn_Slice_Str16Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array, Dqn_Str16 separator);
Dqn_Str16 Dqn_Slice_Str16RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array);
#endif // !defined(DQN_NO_SLICE) #endif // !defined(DQN_NO_SLICE)
#if !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_DSMAP)
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size); template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags);
template <typename T> void Dqn_DSMap_Deinit (Dqn_DSMap<T> *map, Dqn_ZeroMem zero_mem); template <typename T> void Dqn_DSMap_Deinit (Dqn_DSMap<T> *map, Dqn_ZeroMem zero_mem);
template <typename T> bool Dqn_DSMap_IsValid (Dqn_DSMap<T> const *map); template <typename T> bool Dqn_DSMap_IsValid (Dqn_DSMap<T> const *map);
template <typename T> uint32_t Dqn_DSMap_Hash (Dqn_DSMap<T> const *map, Dqn_DSMapKey key); template <typename T> uint32_t Dqn_DSMap_Hash (Dqn_DSMap<T> const *map, Dqn_DSMapKey key);
@ -305,27 +314,28 @@ template <typename T> Dqn_DSMapResult<T> Dqn_DSMap
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_FindKeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 key); template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_FindKeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 key);
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key); template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key);
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key, T const &value); template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key, T const &value);
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8Copy (Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key);
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8Copy (Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key, T const &value);
template <typename T> bool Dqn_DSMap_Resize (Dqn_DSMap<T> *map, uint32_t size); template <typename T> bool Dqn_DSMap_Resize (Dqn_DSMap<T> *map, uint32_t size);
template <typename T> bool Dqn_DSMap_Erase (Dqn_DSMap<T> *map, Dqn_DSMapKey key); template <typename T> bool Dqn_DSMap_Erase (Dqn_DSMap<T> *map, Dqn_DSMapKey key);
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap<T> const *map, void const *data, uint32_t size); template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap<T> const *map, void const *data, uint32_t size);
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash (Dqn_DSMap<T> const *map, void const *data, uint32_t size);
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap<T> const *map, uint64_t u64); template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap<T> const *map, uint64_t u64);
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 string); template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 string);
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy (Dqn_DSMap<T> const *map, Dqn_Arena *arena, Dqn_Str8 string);
#define Dqn_DSMap_KeyCStr8(map, string) Dqn_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1) #define Dqn_DSMap_KeyCStr8(map, string) Dqn_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1)
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash (uint64_t u64); DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash (uint64_t u64);
DQN_API bool Dqn_DSMap_KeyEquals (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); DQN_API bool Dqn_DSMap_KeyEquals (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs);
DQN_API bool operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); DQN_API bool operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs);
#endif // !defined(DQN_NO_DSMAP) #endif // !defined(DQN_NO_DSMAP)
#if !defined(DQN_NO_LIST) #if !defined(DQN_NO_LIST)
template <typename T> Dqn_List<T> Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size); template <typename T> Dqn_List<T> Dqn_List_Init (Dqn_usize chunk_size);
template <typename T, size_t N> Dqn_List<T> Dqn_List_InitCArray (Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]); template <typename T, size_t N> Dqn_List<T> Dqn_List_InitCArray (Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]);
template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk); template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk);
template <typename T> bool Dqn_List_Iterate (Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index); template <typename T> bool Dqn_List_Iterate (Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index);
template <typename T> T * Dqn_List_Make (Dqn_List<T> *list, Dqn_usize count); template <typename T> T * Dqn_List_MakeArena (Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count);
template <typename T> T * Dqn_List_Add (Dqn_List<T> *list, T const &value); template <typename T> T * Dqn_List_MakePool (Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count);
template <typename T> void Dqn_List_AddList (Dqn_List<T> *list, Dqn_List<T> other); template <typename T> T * Dqn_List_AddArena (Dqn_List<T> *list, Dqn_Arena *arena, T const &value);
template <typename T> T * Dqn_List_AddPool (Dqn_List<T> *list, Dqn_ChunkPool *pool, T const &value);
template <typename T> void Dqn_List_AddListArena (Dqn_List<T> *list, Dqn_Arena *arena, Dqn_List<T> other);
template <typename T> void Dqn_List_AddListArena (Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_List<T> other);
template <typename T> Dqn_Slice<T> Dqn_List_ToSliceCopy (Dqn_List<T> const *list, Dqn_Arena* arena); template <typename T> Dqn_Slice<T> Dqn_List_ToSliceCopy (Dqn_List<T> const *list, Dqn_Arena* arena);
#endif // !defined(DQN_NO_LIST) #endif // !defined(DQN_NO_LIST)
@ -932,19 +942,22 @@ template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize s
uint32_t const DQN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; uint32_t const DQN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49;
uint32_t const DQN_DS_MAP_SENTINEL_SLOT = 0; uint32_t const DQN_DS_MAP_SENTINEL_SLOT = 0;
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size) template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags)
{ {
Dqn_DSMap<T> result = {}; Dqn_DSMap<T> result = {};
if (!DQN_CHECKF(Dqn_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size)) if (!DQN_CHECKF(Dqn_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
return result; return result;
if (!arena) if (!DQN_CHECKF(size > 0, "Non-zero size must be given"))
return result;
if (!DQN_CHECK(arena))
return result; return result;
result.arena = arena; result.arena = arena;
result.hash_to_slot = Dqn_Arena_NewArray(result.arena, uint32_t, size, Dqn_ZeroMem_No); result.hash_to_slot = Dqn_Arena_NewArray(result.arena, uint32_t, size, Dqn_ZeroMem_Yes);
result.slots = Dqn_Arena_NewArray(result.arena, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_No); result.slots = Dqn_Arena_NewArray(result.arena, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_Yes);
result.occupied = 1; // For sentinel result.occupied = 1; // For sentinel
result.size = size; result.size = size;
result.initial_size = size; result.initial_size = size;
result.flags = flags;
DQN_ASSERTF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?"); DQN_ASSERTF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
return result; return result;
} }
@ -980,7 +993,12 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
return result; return result;
if (key.type == Dqn_DSMapKeyType_U64NoHash) { if (key.type == Dqn_DSMapKeyType_U64NoHash) {
result = DQN_CAST(uint32_t)key.payload.u64; result = DQN_CAST(uint32_t)key.u64;
return result;
}
if (key.type == Dqn_DSMapKeyType_BufferAsU64NoHash) {
result = key.hash;
return result; return result;
} }
@ -998,17 +1016,18 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
uint32_t len = 0; uint32_t len = 0;
uint32_t h = seed; uint32_t h = seed;
switch (key.type) { switch (key.type) {
case Dqn_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
case Dqn_DSMapKeyType_U64NoHash: DQN_INVALID_CODE_PATH; /*FALLTHRU*/ case Dqn_DSMapKeyType_U64NoHash: DQN_INVALID_CODE_PATH; /*FALLTHRU*/
case Dqn_DSMapKeyType_Invalid: break; case Dqn_DSMapKeyType_Invalid: break;
case Dqn_DSMapKeyType_Buffer: case Dqn_DSMapKeyType_Buffer:
key_ptr = DQN_CAST(char const *)key.payload.buffer.data; key_ptr = DQN_CAST(char const *)key.buffer_data;
len = key.payload.buffer.size; len = key.buffer_size;
break; break;
case Dqn_DSMapKeyType_U64: case Dqn_DSMapKeyType_U64:
key_ptr = DQN_CAST(char const *)&key.payload.u64; key_ptr = DQN_CAST(char const *)&key.u64;
len = sizeof(key.payload.u64); len = sizeof(key.u64);
break; break;
} }
@ -1048,6 +1067,7 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
template <typename T> template <typename T>
uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap<T> const *map, Dqn_DSMapKey key) uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
{ {
DQN_ASSERT(key.type != Dqn_DSMapKeyType_Invalid);
uint32_t result = DQN_DS_MAP_SENTINEL_SLOT; uint32_t result = DQN_DS_MAP_SENTINEL_SLOT;
if (!Dqn_DSMap_IsValid(map)) if (!Dqn_DSMap_IsValid(map))
return result; return result;
@ -1101,6 +1121,11 @@ Dqn_DSMapResult<T> Dqn_DSMap_Make(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
} else { } else {
result.slot = map->slots + map->hash_to_slot[index]; result.slot = map->slots + map->hash_to_slot[index];
result.slot->key = key; // NOTE: Assign key to new slot result.slot->key = key; // NOTE: Assign key to new slot
if ((result.slot->key.type == Dqn_DSMapKeyType_Buffer ||
result->slot.key.type == Dqn_DSMapKeyType_BufferAsU64NoHash) &&
!key.no_copy_buffer) {
result.slot->key.buffer_data = Dqn_Arena_Copy(map->arena, key.buffer_data, key.buffer_size, 1);
}
} }
} else { } else {
result.slot = map->slots + map->hash_to_slot[index]; result.slot = map->slots + map->hash_to_slot[index];
@ -1108,6 +1133,7 @@ Dqn_DSMapResult<T> Dqn_DSMap_Make(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
} }
result.value = &result.slot->value; result.value = &result.slot->value;
DQN_ASSERT(result.slot->key.type != Dqn_DSMapKeyType_Invalid);
return result; return result;
} }
@ -1171,30 +1197,6 @@ Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8(Dqn_DSMap<T> *map, Dqn_Str8 key, T const
return result; return result;
} }
template <typename T>
Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8Copy(Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key)
{
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, arena, key);
Dqn_DSMapResult<T> result = Dqn_DSMap_Make(map, map_key);
// NOTE: If it already exists then we already have the key, we can deallocate
if (result.found)
Dqn_Arena_TempMemEnd(temp_mem);
return result;
}
template <typename T>
Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8Copy(Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key, T const &value)
{
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, arena, key);
Dqn_DSMapResult<T> result = Dqn_DSMap_Set(map, map_key);
// NOTE: If it already exists then we already have the key, we can deallocate
if (result.found)
Dqn_Arena_TempMemEnd(temp_mem);
return result;
}
template <typename T> template <typename T>
bool Dqn_DSMap_Resize(Dqn_DSMap<T> *map, uint32_t size) bool Dqn_DSMap_Resize(Dqn_DSMap<T> *map, uint32_t size)
{ {
@ -1205,20 +1207,21 @@ bool Dqn_DSMap_Resize(Dqn_DSMap<T> *map, uint32_t size)
Dqn_Arena new_arena = {}; Dqn_Arena new_arena = {};
new_arena.flags = prev_arena->flags; new_arena.flags = prev_arena->flags;
Dqn_DSMap<T> new_map = Dqn_DSMap_Init<T>(&new_arena, size); Dqn_DSMap<T> new_map = Dqn_DSMap_Init<T>(&new_arena, size, map->flags);
if (!Dqn_DSMap_IsValid(&new_map)) if (!Dqn_DSMap_IsValid(&new_map))
return false; return false;
new_map.initial_size = map->initial_size; new_map.initial_size = map->initial_size;
for (uint32_t old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) { for (uint32_t old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) {
Dqn_DSMapSlot<T> *old_slot = map->slots + old_index; Dqn_DSMapSlot<T> *old_slot = map->slots + old_index;
if (old_slot->key.type != Dqn_DSMapKeyType_Invalid) { Dqn_DSMapKey old_key = old_slot->key;
Dqn_DSMap_Set(&new_map, old_slot->key, old_slot->value); if (old_key.type == Dqn_DSMapKeyType_Invalid)
} continue;
Dqn_DSMap_Set(&new_map, old_key, old_slot->value);
} }
DQN_MEMCPY(new_map.slots, map->slots, sizeof(*map->slots) * map->occupied); if ((map->flags & Dqn_DSMapFlags_DontFreeArenaOnResize) == 0)
Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No); Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No);
*map = new_map; // Update the map inplace *map = new_map; // Update the map inplace
map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed
*map->arena = new_arena; // Re-init the old arena with the new data *map->arena = new_arena; // Re-init the old arena with the new data
@ -1269,14 +1272,17 @@ bool Dqn_DSMap_Erase(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
// We will now fill in the vacant spot that we erased using the last // We will now fill in the vacant spot that we erased using the last
// element in the slot list. // element in the slot list.
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) { if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
// NOTE: Copy in last slot to the erase slot uint32_t last_index = map->occupied - 1;
Dqn_DSMapSlot<T> *last_slot = map->slots + map->occupied - 1; if (last_index != slot_index) {
map->slots[slot_index] = *last_slot; // NOTE: Copy in last slot to the erase slot
Dqn_DSMapSlot<T> *last_slot = map->slots + last_index;
map->slots[slot_index] = *last_slot;
// NOTE: Update the hash-to-slot mapping for the value that was copied in // NOTE: Update the hash-to-slot mapping for the value that was copied in
uint32_t hash_to_slot_index = Dqn_DSMap_HashToSlotIndex(map, last_slot->key); uint32_t hash_to_slot_index = Dqn_DSMap_HashToSlotIndex(map, last_slot->key);
map->hash_to_slot[hash_to_slot_index] = slot_index; map->hash_to_slot[hash_to_slot_index] = slot_index;
*last_slot = {}; // TODO: Optional? *last_slot = {}; // TODO: Optional?
}
} }
} }
@ -1289,53 +1295,52 @@ bool Dqn_DSMap_Erase(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
} }
template <typename T> template <typename T>
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap<T> const *map, void const *data, uint32_t size) Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap<T> const *map, void const *data, Dqn_usize size)
{
Dqn_DSMapKey result = {};
result.type = Dqn_DSMapKeyType_Buffer;
result.payload.buffer.data = data;
result.payload.buffer.size = size;
result.hash = Dqn_DSMap_Hash(map, result);
return result;
}
template <typename T>
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap<T> const *map, uint64_t u64)
{ {
DQN_ASSERT(size > 0 && size <= UINT32_MAX);
Dqn_DSMapKey result = {}; Dqn_DSMapKey result = {};
result.type = Dqn_DSMapKeyType_U64; result.type = Dqn_DSMapKeyType_Buffer;
result.payload.u64 = u64; result.buffer_data = data;
result.buffer_size = DQN_CAST(uint32_t) size;
result.hash = Dqn_DSMap_Hash(map, result); result.hash = Dqn_DSMap_Hash(map, result);
return result; return result;
} }
template <typename T> template <typename T>
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap<T> const *map, Dqn_Str8 string) Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash(Dqn_DSMap<T> const *map, void const *data, uint32_t size)
{ {
DQN_ASSERT(string.size > 0 && string.size <= UINT32_MAX); Dqn_DSMapKey result = {};
Dqn_DSMapKey result = {}; result.type = Dqn_DSMapKeyType_BufferAsU64NoHash;
result.type = Dqn_DSMapKeyType_Buffer; result.buffer_data = data;
result.payload.buffer.data = string.data; result.buffer_size = DQN_CAST(uint32_t) size;
result.payload.buffer.size = DQN_CAST(uint32_t)string.size; DQN_ASSERT(size >= sizeof(result.hash));
result.hash = Dqn_DSMap_Hash(map, result); DQN_MEMCPY(&result.hash, data, sizeof(result.hash));
return result; return result;
} }
template <typename T> template <typename T>
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy(Dqn_DSMap<T> const *map, Dqn_Arena *arena, Dqn_Str8 string) Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap<T> const *map, uint64_t u64)
{ {
Dqn_Str8 copy = Dqn_Str8_Copy(arena, string); Dqn_DSMapKey result = {};
Dqn_DSMapKey result = Dqn_DSMap_KeyStr8(map, copy); result.type = Dqn_DSMapKeyType_U64;
result.u64 = u64;
result.hash = Dqn_DSMap_Hash(map, result);
return result;
}
template <typename T>
Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap<T> const *map, Dqn_Str8 string)
{
Dqn_DSMapKey result = Dqn_DSMap_KeyBuffer(map, string.data, string.size);
return result; return result;
} }
#endif // !defined(DQN_NO_DSMAP) #endif // !defined(DQN_NO_DSMAP)
#if !defined(DQN_NO_LIST) #if !defined(DQN_NO_LIST)
// NOTE: [$LIST] Dqn_List ////////////////////////////////////////////////////////////////////////// // NOTE: [$LIST] Dqn_List //////////////////////////////////////////////////////////////////////////
template <typename T> Dqn_List<T> Dqn_List_Init(Dqn_Arena *arena, Dqn_usize chunk_size) template <typename T> Dqn_List<T> Dqn_List_Init(Dqn_usize chunk_size)
{ {
Dqn_List<T> result = {}; Dqn_List<T> result = {};
result.arena = arena;
result.chunk_size = chunk_size; result.chunk_size = chunk_size;
return result; return result;
} }
@ -1356,31 +1361,68 @@ template <typename T> Dqn_List<T> Dqn_List_InitSliceCopy(Dqn_Arena *arena, Dqn_u
return result; return result;
} }
template <typename T> DQN_API T *Dqn_List_Make(Dqn_List<T> *list, Dqn_usize count) template <typename T> DQN_API bool Dqn_List_AttachTail_(Dqn_List<T> *list, Dqn_ListChunk<T> *tail)
{
if (!tail)
return false;
if (list->tail) {
list->tail->next = tail;
tail->prev = list->tail;
}
list->tail = tail;
if (!list->head)
list->head = list->tail;
return true;
}
template <typename T> DQN_API Dqn_ListChunk<T> *Dqn_List_AllocArena_(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count)
{
auto *result = Dqn_Arena_New(arena, Dqn_ListChunk<T>, Dqn_ZeroMem_Yes);
Dqn_ArenaTempMem tmem = Dqn_Arena_TempMemBegin(arena);
if (!result)
return nullptr;
Dqn_usize items = DQN_MAX(list->chunk_size, count);
result->data = Dqn_Arena_NewArray(arena, T, items, Dqn_ZeroMem_Yes);
result->size = items;
if (!result->data) {
Dqn_Arena_TempMemEnd(tmem);
result = nullptr;
}
Dqn_List_AttachTail_(list, result);
return result;
}
template <typename T> DQN_API Dqn_ListChunk<T> *Dqn_List_AllocPool_(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count)
{
auto *result = Dqn_ChunkPool_New(pool, Dqn_ListChunk<T>);
if (!result)
return nullptr;
Dqn_usize items = DQN_MAX(list->chunk_size, count);
result->data = Dqn_ChunkPool_NewArray(pool, T, items);
result->size = items;
if (!result->data) {
Dqn_ChunkPool_Dealloc(result);
result = nullptr;
}
Dqn_List_AttachTail_(list, result);
return result;
}
template <typename T> DQN_API T *Dqn_List_MakeArena(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count)
{ {
if (list->chunk_size == 0) if (list->chunk_size == 0)
list->chunk_size = 128; list->chunk_size = 128;
if (!list->tail || (list->tail->count + count) > list->tail->size) { if (!list->tail || (list->tail->count + count) > list->tail->size) {
auto *tail = Dqn_Arena_New(list->arena, Dqn_ListChunk<T>, Dqn_ZeroMem_Yes); if (!Dqn_List_AllocArena_(list, arena, count))
if (!tail)
return nullptr;
Dqn_usize items = DQN_MAX(list->chunk_size, count);
tail->data = Dqn_Arena_NewArray(list->arena, T, items, Dqn_ZeroMem_Yes);
tail->size = items;
if (!tail->data)
return nullptr; return nullptr;
if (list->tail) {
list->tail->next = tail;
tail->prev = list->tail;
}
list->tail = tail;
if (!list->head)
list->head = list->tail;
} }
T *result = list->tail->data + list->tail->count; T *result = list->tail->data + list->tail->count;
@ -1389,9 +1431,32 @@ template <typename T> DQN_API T *Dqn_List_Make(Dqn_List<T> *list, Dqn_usize coun
return result; return result;
} }
template <typename T> DQN_API T *Dqn_List_Add(Dqn_List<T> *list, T const &value) template <typename T> DQN_API T *Dqn_List_MakePool(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count)
{ {
T *result = Dqn_List_Make(list, 1); if (list->chunk_size == 0)
list->chunk_size = 128;
if (!list->tail || (list->tail->count + count) > list->tail->size) {
if (!Dqn_List_AllocPool_(list, pool, count))
return nullptr;
}
T *result = list->tail->data + list->tail->count;
list->tail->count += count;
list->count += count;
return result;
}
template <typename T> DQN_API T *Dqn_List_AddArena(Dqn_List<T> *list, Dqn_Arena *arena, T const &value)
{
T *result = Dqn_List_MakeArena(list, arena, 1);
*result = value;
return result;
}
template <typename T> DQN_API T *Dqn_List_AddPool(Dqn_List<T> *list, Dqn_ChunkPool *pool, T const &value)
{
T *result = Dqn_List_MakePool(list, pool, 1);
*result = value; *result = value;
return result; return result;
} }
@ -1407,14 +1472,22 @@ template <typename T, size_t N> DQN_API bool Dqn_List_AddCArray(Dqn_List<T> *lis
return true; return true;
} }
template <typename T> DQN_API void Dqn_List_AddList(Dqn_List<T> *list, Dqn_List<T> other) template <typename T> DQN_API void Dqn_List_AddListArena(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_List<T> other)
{ {
if (!list || list->chunk_size <= 0) if (!list || list->chunk_size <= 0)
return; return;
// TODO(doyle): Copy chunk by chunk // TODO(doyle): Copy chunk by chunk
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); ) for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); )
Dqn_List_Add(list, *it.data); Dqn_List_AddArena(list, arena, *it.data);
}
template <typename T> DQN_API void Dqn_List_AddListPool(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_List<T> other)
{
if (!list || list->chunk_size <= 0)
return;
// TODO(doyle): Copy chunk by chunk
for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); )
Dqn_List_AddPool(list, pool, *it.data);
} }
template <typename T> DQN_API bool Dqn_List_Iterate(Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index) template <typename T> DQN_API bool Dqn_List_Iterate(Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index)
@ -1497,8 +1570,11 @@ template <typename T> Dqn_Slice<T> Dqn_List_ToSliceCopy(Dqn_List<T> const *list,
Dqn_Slice<T> result = Dqn_Slice_Alloc<T>(arena, list->count, Dqn_ZeroMem_No); Dqn_Slice<T> result = Dqn_Slice_Alloc<T>(arena, list->count, Dqn_ZeroMem_No);
if (result.size) { if (result.size) {
Dqn_usize slice_index = 0; Dqn_usize slice_index = 0;
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(6011) // Dereferencing NULL pointer 'x'
for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate<T>(DQN_CAST(Dqn_List<T> *)list, &it, 0);) for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate<T>(DQN_CAST(Dqn_List<T> *)list, &it, 0);)
result.data[slice_index++] = *it.data; result.data[slice_index++] = *it.data;
DQN_MSVC_WARNING_POP
DQN_ASSERT(slice_index == result.size); DQN_ASSERT(slice_index == result.size);
} }
return result; return result;

View File

@ -67,8 +67,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
g_dqn_library->win32_sym_initialised = true; g_dqn_library->win32_sym_initialised = true;
SymSetOptions(SYMOPT_LOAD_LINES); SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_WinError error = Dqn_Win_LastError(tmem.arena);
Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg)); Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg));
} }
} }
@ -115,8 +115,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip) DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(scratch.arena, limit); Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(tmem.arena, limit);
Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip); Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip);
return result; return result;
} }
@ -128,7 +128,7 @@ static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const
for (Dqn_usize index = skip; index < walk->size; index++) { for (Dqn_usize index = skip; index < walk->size; index++) {
raw_frame.base_addr = walk->base_addr[index]; raw_frame.base_addr = walk->base_addr[index];
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame); Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
Dqn_Str8Builder_AppendF(builder, "%.*s(%zu): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (DQN_CAST(int)index == walk->size - 1) ? "" : "\n"); Dqn_Str8Builder_AddF(builder, "%.*s(%zu): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (DQN_CAST(int)index == walk->size - 1) ? "" : "\n");
} }
} }
@ -167,9 +167,8 @@ DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8(Dqn_Arena *arena, Dqn_StackTraceW
if (!walk || !arena) if (!walk || !arena)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
builder.arena = scratch.arena;
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip); Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
result = Dqn_Str8Builder_Build(&builder, arena); result = Dqn_Str8Builder_Build(&builder, arena);
return result; return result;
@ -181,9 +180,8 @@ DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const
if (!walk) if (!walk)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
builder.arena = scratch.arena;
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip); Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
result = Dqn_Str8Builder_BuildCRT(&builder); result = Dqn_Str8Builder_BuildCRT(&builder);
return result; return result;
@ -196,8 +194,8 @@ DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena
if (!arena) if (!arena)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit); Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(tmem.arena, limit);
if (!walk.size) if (!walk.size)
return result; return result;
@ -259,8 +257,8 @@ DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn
DQN_API void Dqn_StackTrace_Print(uint16_t limit) DQN_API void Dqn_StackTrace_Print(uint16_t limit)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit); Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(tmem.arena, limit);
for (Dqn_StackTraceFrame &frame : stack_trace) for (Dqn_StackTraceFrame &frame : stack_trace)
Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name)); Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
} }

View File

@ -48,6 +48,14 @@ void Dqn_Docs_Demo()
} }
} }
// NOTE: Dqn_BytesToHex ////////////////////////////////////////////////////////////////////////
{
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
unsigned char bytes[2] = {0xFA, 0xCE};
Dqn_Str8 hex = Dqn_BytesToHex(tmem.arena, bytes, sizeof(bytes));
DQN_ASSERT(hex == DQN_STR8("face")); // NOTE: Guaranteed to be null-terminated
}
// NOTE: DQN_CHECK ///////////////////////////////////////////////////////////////////////////// // NOTE: DQN_CHECK /////////////////////////////////////////////////////////////////////////////
// //
// Check the expression trapping in debug, whilst in release- trapping is // Check the expression trapping in debug, whilst in release- trapping is
@ -140,8 +148,8 @@ void Dqn_Docs_Demo()
// A 'Deinit' of the map will similarly deallocate the passed in arena (as // A 'Deinit' of the map will similarly deallocate the passed in arena (as
// the map takes ownership of the arena). // the map takes ownership of the arena).
Dqn_Arena arena = {}; Dqn_Arena arena = {};
Dqn_DSMap<int> map = Dqn_DSMap_Init<int>(&arena, /*size*/ 1024); // Size must be PoT! Dqn_DSMap<int> map = Dqn_DSMap_Init<int>(&arena, /*size*/ 1024, Dqn_DSMapFlags_Nil); // Size must be PoT!
DQN_ASSERT(Dqn_DSMap_IsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure) DQN_ASSERT(Dqn_DSMap_IsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure)
// NOTE: Dqn_DSMap_KeyCStringLit /////////////////////////////////////////////////////////// // NOTE: Dqn_DSMap_KeyCStringLit ///////////////////////////////////////////////////////////
// NOTE: Dqn_DSMap_KeyU64 /////////////////////////////////////////////////////////// // NOTE: Dqn_DSMap_KeyU64 ///////////////////////////////////////////////////////////
@ -204,7 +212,7 @@ void Dqn_Docs_Demo()
int *it_value = &it->value; int *it_value = &it->value;
DQN_ASSERT(*it_value == 0xCAFE); DQN_ASSERT(*it_value == 0xCAFE);
DQN_ASSERT(Dqn_Str8_Init(it_key.payload.buffer.data, it_key.payload.buffer.size) == DQN_STR8("Sample Key")); DQN_ASSERT(Dqn_Str8_Init(it_key.buffer_data, it_key.buffer_size) == DQN_STR8("Sample Key"));
} }
// NOTE: Dqn_DSMap_Erase /////////////////////////////////////////////////////////////////// // NOTE: Dqn_DSMap_Erase ///////////////////////////////////////////////////////////////////
@ -327,6 +335,15 @@ void Dqn_Docs_Demo()
// The lifetime of the slice is bound to the lifetime of the FStr8 and is // The lifetime of the slice is bound to the lifetime of the FStr8 and is
// invalidated when the FStr8 is. // invalidated when the FStr8 is.
// NOTE: Dqn_HexToBytes ////////////////////////////////////////////////////////////////////////
{
unsigned char bytes[2];
Dqn_usize bytes_written = Dqn_HexToBytesPtr(DQN_STR8("0xFACE"), bytes, sizeof(bytes));
DQN_ASSERT(bytes_written == 2);
DQN_ASSERT(bytes[0] == 0xFA);
DQN_ASSERT(bytes[1] == 0xCE);
}
// NOTE: Dqn_JSONBuilder_Build ///////////////////////////////////////////////////////////////// // NOTE: Dqn_JSONBuilder_Build /////////////////////////////////////////////////////////////////
// //
// Convert the internal JSON buffer in the builder into a string. // Convert the internal JSON buffer in the builder into a string.
@ -371,8 +388,8 @@ void Dqn_Docs_Demo()
// NOTE: Dqn_List_Iterate ////////////////////////////////////////////////////////////////////// // NOTE: Dqn_List_Iterate //////////////////////////////////////////////////////////////////////
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_List<int> list = Dqn_List_Init<int>(scratch.arena, /*chunk_size*/ 128); Dqn_List<int> list = Dqn_List_Init<int>(/*chunk_size*/ 128);
for (Dqn_ListIterator<int> it = {}; Dqn_List_Iterate(&list, &it, 0);) { for (Dqn_ListIterator<int> it = {}; Dqn_List_Iterate(&list, &it, 0);) {
int *item = it.data; int *item = it.data;
(void)item; (void)item;
@ -450,7 +467,7 @@ void Dqn_Docs_Demo()
// If 'tmp_path' is written to successfuly, the file will be copied over into // If 'tmp_path' is written to successfuly, the file will be copied over into
// 'path'. // 'path'.
if (0) { if (0) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_ErrorSink *error = Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode_Nil); Dqn_ErrorSink *error = Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode_Nil);
Dqn_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"), error); Dqn_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"), error);
Dqn_ErrorSink_EndAndLogErrorF(error, ""); Dqn_ErrorSink_EndAndLogErrorF(error, "");
@ -550,12 +567,11 @@ void Dqn_Docs_Demo()
// that can be converted into a duration. // that can be converted into a duration.
// //
// This profiler uses a double buffer scheme for storing profiling markers. // This profiler uses a double buffer scheme for storing profiling markers.
// After an application's typical update/frame cycle you can swap the profiler's // After an application's typical update/frame cycle you can swap the
// buffer whereby the front buffer contains the previous frames profiling // profiler's buffer whereby the front buffer contains the previous frames
// metrics and the back buffer will be populated with the new frame's profiling // profiling metrics and the back buffer will be populated with the new
// metrics. // frame's profiling metrics.
{ {
uint64_t tsc_per_seconds = Dqn_OS_EstimateTSCPerSecond(/*duration_ms_to_gauge_tsc_frequency*/ 100);
enum Zone { Zone_MainLoop, Zone_Count }; enum Zone { Zone_MainLoop, Zone_Count };
Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop); Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop);
@ -573,22 +589,34 @@ void Dqn_Docs_Demo()
// the front buffer which contain the metrics that you can visualise // the front buffer which contain the metrics that you can visualise
// regarding the most profiling metrics recorded. // regarding the most profiling metrics recorded.
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Front); // NOTE: Dqn_Profiler_ReadBuffer ///////////////////////////////////////////////////////////
for (size_t index = 0; index < Zone_Count; index++) { //
Dqn_ProfilerAnchor *anchor = anchors + index; // Retrieve the buffer of anchors of which there are
// `DQN_PROFILER_ANCHOR_BUFFER_SIZE` anchors from the most recent run
// Print the result like so // of the profiler after you have called `SwapAnchorBuffer` to trigger
if (0) { // the double buffer
printf("%.*s[%u] %" PRIu64 " cycles (%.1fms)\n", Dqn_ProfilerAnchor *read_anchors = Dqn_Profiler_ReadBuffer();
DQN_STR_FMT(anchor->name), for (Dqn_usize index = 0; index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
anchor->hit_count, Dqn_ProfilerAnchor *anchor = read_anchors + index;
anchor->tsc_inclusive, if (Dqn_Str8_HasData(anchor->name)) {
anchor->tsc_inclusive * tsc_per_seconds * 1000.f); // ...
} }
} }
// NOTE: Dqn_Profiler_WriteBuffer //////////////////////////////////////////////////////////
//
// Same as `ReadBuffer` however we return the buffer that the profiler
// is currently writing anchors into.
Dqn_ProfilerAnchor *write_anchors = Dqn_Profiler_WriteBuffer();
for (Dqn_usize index = 0; index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
Dqn_ProfilerAnchor *anchor = write_anchors + index;
if (Dqn_Str8_HasData(anchor->name)) {
// ...
}
}
Dqn_Profiler_EndZone(profiler_zone_main_update); Dqn_Profiler_EndZone(profiler_zone_main_update);
Dqn_Profiler_SwapAnchorBuffer(); // Should occur after all profiling zones are ended! Dqn_Profiler_SwapAnchorBuffer(); // Should occur after all profiling zones are ended!
*g_dqn_library->profiler = {}; *g_dqn_library->profiler = {};
} }
#endif // !defined(DQN_NO_PROFILER) #endif // !defined(DQN_NO_PROFILER)
@ -672,7 +700,7 @@ void Dqn_Docs_Demo()
// the debug APIs are aware of how to resolve the new addresses imported // the debug APIs are aware of how to resolve the new addresses imported
// into the address space. // into the address space.
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
// NOTE: Dqn_StackTrace_Walk /////////////////////////////////////////////////////////////// // NOTE: Dqn_StackTrace_Walk ///////////////////////////////////////////////////////////////
// //
@ -680,7 +708,7 @@ void Dqn_Docs_Demo()
// functions on the call-stack at the current instruction pointer. The // functions on the call-stack at the current instruction pointer. The
// addresses are stored in order from the current executing function // addresses are stored in order from the current executing function
// first to the most ancestor function last in the walk. // first to the most ancestor function last in the walk.
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, /*depth limit*/ 128); Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(tmem.arena, /*depth limit*/ 128);
// Loop over the addresses produced in the stack trace // Loop over the addresses produced in the stack trace
for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) { for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) {
@ -689,7 +717,7 @@ void Dqn_Docs_Demo()
// //
// Converts the base address into a human readable stack trace // Converts the base address into a human readable stack trace
// entry (e.g. address, line number, file and function name). // entry (e.g. address, line number, file and function name).
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(scratch.arena, it.raw_frame); Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(tmem.arena, it.raw_frame);
// You may then print out the frame like so // You may then print out the frame like so
if (0) if (0)
@ -706,7 +734,7 @@ void Dqn_Docs_Demo()
// Helper function to create a stack trace and automatically convert the // Helper function to create a stack trace and automatically convert the
// raw frames into human readable frames. This function effectively // raw frames into human readable frames. This function effectively
// calls 'Walk' followed by 'RawFrameToFrame'. // calls 'Walk' followed by 'RawFrameToFrame'.
Dqn_Slice<Dqn_StackTraceFrame> frames = Dqn_StackTrace_GetFrames(scratch.arena, /*depth limit*/ 128); Dqn_Slice<Dqn_StackTraceFrame> frames = Dqn_StackTrace_GetFrames(tmem.arena, /*depth limit*/ 128);
(void)frames; (void)frames;
} }
@ -719,8 +747,8 @@ void Dqn_Docs_Demo()
// The returned string's 'size' member variable does *not* include this // The returned string's 'size' member variable does *not* include this
// additional null-terminating byte. // additional null-terminating byte.
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, /*size*/ 1, Dqn_ZeroMem_Yes); Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, /*size*/ 1, Dqn_ZeroMem_Yes);
DQN_ASSERT(string.size == 1); DQN_ASSERT(string.size == 1);
DQN_ASSERT(string.data[string.size] == 0); // It is null-terminated! DQN_ASSERT(string.data[string.size] == 0); // It is null-terminated!
} }
@ -799,12 +827,12 @@ void Dqn_Docs_Demo()
// always be a newly allocated copy, irrespective of if any replacements // always be a newly allocated copy, irrespective of if any replacements
// were done or not. // were done or not.
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_Str8_Replace(/*string*/ DQN_STR8("Foo Foo Bar"), Dqn_Str8 string = Dqn_Str8_Replace(/*string*/ DQN_STR8("Foo Foo Bar"),
/*find*/ DQN_STR8("Foo"), /*find*/ DQN_STR8("Foo"),
/*replace*/ DQN_STR8("Moo"), /*replace*/ DQN_STR8("Moo"),
/*start_index*/ 1, /*start_index*/ 1,
/*arena*/ scratch.arena, /*arena*/ tmem.arena,
/*eq_case*/ Dqn_Str8EqCase_Sensitive); /*eq_case*/ Dqn_Str8EqCase_Sensitive);
DQN_ASSERT(string == DQN_STR8("Foo Moo Bar")); DQN_ASSERT(string == DQN_STR8("Foo Moo Bar"));
} }
@ -817,8 +845,8 @@ void Dqn_Docs_Demo()
// Reverse segment delimits the string counting 'segment_size' from the back // Reverse segment delimits the string counting 'segment_size' from the back
// of the string. // of the string.
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_Str8_Segment(scratch.arena, /*string*/ DQN_STR8("123456789"), /*segment_size*/ 3, /*segment_char*/ ','); Dqn_Str8 string = Dqn_Str8_Segment(tmem.arena, /*string*/ DQN_STR8("123456789"), /*segment_size*/ 3, /*segment_char*/ ',');
DQN_ASSERT(string == DQN_STR8("123,456,789")); DQN_ASSERT(string == DQN_STR8("123,456,789"));
} }
@ -826,9 +854,9 @@ void Dqn_Docs_Demo()
{ {
// Splits the string at each delimiter into substrings occuring prior and // Splits the string at each delimiter into substrings occuring prior and
// after until the next delimiter. // after until the next delimiter.
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
{ {
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena, Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena,
/*string*/ DQN_STR8("192.168.8.1"), /*string*/ DQN_STR8("192.168.8.1"),
/*delimiter*/ DQN_STR8("."), /*delimiter*/ DQN_STR8("."),
/*mode*/ Dqn_Str8SplitIncludeEmptyStrings_No); /*mode*/ Dqn_Str8SplitIncludeEmptyStrings_No);
@ -839,7 +867,7 @@ void Dqn_Docs_Demo()
// You can include empty strings that occur when splitting by setting // You can include empty strings that occur when splitting by setting
// the split mode to include empty strings. // the split mode to include empty strings.
{ {
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena, Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena,
/*string*/ DQN_STR8("a--b"), /*string*/ DQN_STR8("a--b"),
/*delimiter*/ DQN_STR8("-"), /*delimiter*/ DQN_STR8("-"),
/*mode*/ Dqn_Str8SplitIncludeEmptyStrings_Yes); /*mode*/ Dqn_Str8SplitIncludeEmptyStrings_Yes);
@ -953,7 +981,7 @@ void Dqn_Docs_Demo()
// NOTE: Dqn_ThreadContext ///////////////////////////////////////////////////////////////////// // NOTE: Dqn_ThreadContext /////////////////////////////////////////////////////////////////////
// //
// Each thread is assigned in their thread-local storage (TLS) scratch and // Each thread is assigned in their thread-local storage (TLS) tmem and
// permanent arena allocators. These can be used for allocations with a // permanent arena allocators. These can be used for allocations with a
// lifetime scoped to the lexical scope or for storing data permanently // lifetime scoped to the lexical scope or for storing data permanently
// using the arena paradigm. // using the arena paradigm.
@ -961,40 +989,40 @@ void Dqn_Docs_Demo()
// TLS in this implementation is implemented using the `thread_local` C/C++ // TLS in this implementation is implemented using the `thread_local` C/C++
// keyword. // keyword.
// //
// 99% of the time you will want Dqn_Scratch_Get(...) which returns you a // 99% of the time you will want Dqn_TLS_TMem(...) which returns you a
// temporary arena for function lifetime allocations. On scope exit, the // temporary arena for function lifetime allocations. On scope exit, the
// arena is cleared out. // arena is cleared out.
// //
// This library's paradigm revolves heavily around arenas including scratch // This library's paradigm revolves heavily around arenas including tmem
// arenas into child functions for temporary calculations. If an arena is // arenas into child functions for temporary calculations. If an arena is
// passed into a function, this poses a problem sometimes known as // passed into a function, this poses a problem sometimes known as
// 'arena aliasing'. // 'arena aliasing'.
// //
// If an arena aliases another arena (e.g. the arena passed in) is the same // If an arena aliases another arena (e.g. the arena passed in) is the same
// as the scratch arena requested in the function, we risk the scratch arena // as the tmem arena requested in the function, we risk the tmem arena
// on scope exit deallocating memory belonging to the caller. // on scope exit deallocating memory belonging to the caller.
// //
// To avoid this we the 'Dqn_Scratch_Get(...)' API takes in a list of arenas // To avoid this we the 'Dqn_TLS_TMem(...)' API takes in a list of arenas
// to ensure that we provide a scratch arena that *won't* alias with the // to ensure that we provide a tmem arena that *won't* alias with the
// caller's arena. If arena aliasing occurs, with ASAN on, generally // caller's arena. If arena aliasing occurs, with ASAN on, generally
// the library will trap and report use-after-poison once violated. // the library will trap and report use-after-poison once violated.
{ {
Dqn_Scratch scratch_a = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem_a = Dqn_TLS_TMem(nullptr);
// Now imagine we call a function where we pass scratch_a.arena down // Now imagine we call a function where we pass tmem_a.arena down
// into it .. If we call scratch again, we need to pass in the arena // into it .. If we call tmem again, we need to pass in the arena
// to prevent aliasing. // to prevent aliasing.
Dqn_Scratch scratch_b = Dqn_Scratch_Get(scratch_a.arena); Dqn_TLSTMem tmem_b = Dqn_TLS_TMem(tmem_a.arena);
DQN_ASSERT(scratch_a.arena != scratch_b.arena); DQN_ASSERT(tmem_a.arena != tmem_b.arena);
} }
// @proc Dqn_Thread_GetScratch // @proc Dqn_Thread_GetTMem
// @desc Retrieve the per-thread temporary arena allocator that is reset on scope // @desc Retrieve the per-thread temporary arena allocator that is reset on scope
// exit. // exit.
// The scratch arena must be deconflicted with any existing arenas in the // The tmem arena must be deconflicted with any existing arenas in the
// function to avoid trampling over each other's memory. Consider the situation // function to avoid trampling over each other's memory. Consider the situation
// where the scratch arena is passed into the function. Inside the function, if // where the tmem arena is passed into the function. Inside the function, if
// the same arena is reused then, if both arenas allocate, when the inner arena // the same arena is reused then, if both arenas allocate, when the inner arena
// is reset, this will undo the passed in arena's allocations in the function. // is reset, this will undo the passed in arena's allocations in the function.
@ -1010,8 +1038,8 @@ void Dqn_Docs_Demo()
// NOTE: Dqn_U64ToAge ////////////////////////////////////////////////////////////////////////// // NOTE: Dqn_U64ToAge //////////////////////////////////////////////////////////////////////////
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_U64ToAge(scratch.arena, DQN_HOURS_TO_S(2) + DQN_MINS_TO_S(30), Dqn_U64AgeUnit_All); Dqn_Str8 string = Dqn_U64ToAge(tmem.arena, DQN_HOURS_TO_S(2) + DQN_MINS_TO_S(30), Dqn_U64AgeUnit_All);
if (0) // Prints "2hr 30m" if (0) // Prints "2hr 30m"
printf("%.*s", DQN_STR_FMT(string)); printf("%.*s", DQN_STR_FMT(string));
} }
@ -1131,12 +1159,12 @@ void Dqn_Docs_Demo()
if (0) { if (0) {
// Generate the error string for the last Win32 API called that return // Generate the error string for the last Win32 API called that return
// an error value. // an error value.
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_WinError get_last_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError get_last_error = Dqn_Win_LastError(tmem.arena);
printf("Error (%lu): %.*s", get_last_error.code, DQN_STR_FMT(get_last_error.msg)); printf("Error (%lu): %.*s", get_last_error.code, DQN_STR_FMT(get_last_error.msg));
// Alternatively, pass in the error code directly // Alternatively, pass in the error code directly
Dqn_WinError error_msg_for_code = Dqn_Win_ErrorCodeToMsg(scratch.arena, /*error_code*/ 0); Dqn_WinError error_msg_for_code = Dqn_Win_ErrorCodeToMsg(tmem.arena, /*error_code*/ 0);
printf("Error (%lu): %.*s", error_msg_for_code.code, DQN_STR_FMT(error_msg_for_code.msg)); printf("Error (%lu): %.*s", error_msg_for_code.code, DQN_STR_FMT(error_msg_for_code.msg));
} }

View File

@ -143,17 +143,16 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
int spaces = builder->indent_level * spaces_per_indent; int spaces = builder->indent_level * spaces_per_indent;
if (key.size) { if (key.size) {
Dqn_Str8Builder_AppendF(&builder->string_builder, Dqn_Str8Builder_AddF(&builder->string_builder,
"%.*s%*c\"%.*s\": %.*s", "%.*s%*c\"%.*s\": %.*s",
prefix_size, prefix_size,
prefix, prefix,
spaces, spaces,
' ', ' ',
DQN_STR_FMT(key), DQN_STR_FMT(key),
DQN_STR_FMT(value)); DQN_STR_FMT(value));
} else { } else {
Dqn_Str8Builder_AppendF( Dqn_Str8Builder_AddF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
} }
if (item == Dqn_JSONBuilderItem_OpenContainer) if (item == Dqn_JSONBuilderItem_OpenContainer)
@ -164,8 +163,8 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, va_list args) DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, va_list args)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(builder->string_builder.arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(builder->string_builder.arena);
Dqn_Str8 value = Dqn_Str8_InitFV(scratch.arena, value_fmt, args); Dqn_Str8 value = Dqn_Str8_InitFV(tmem.arena, value_fmt, args);
Dqn_JSONBuilder_KeyValue(builder, key, value); Dqn_JSONBuilder_KeyValue(builder, key, value);
} }
@ -765,55 +764,55 @@ DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString(Dqn_U64ByteSizeType type)
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_usize type) DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_U64AgeUnit unit)
{ {
Dqn_Str8 result = {}; Dqn_Str8 result = {};
if (!arena) if (!arena)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = {};
builder.arena = arena; builder.arena = arena;
uint64_t remainder = age_s; uint64_t remainder = age_s;
if (type & Dqn_U64AgeUnit_Year) { if (unit & Dqn_U64AgeUnit_Year) {
Dqn_usize unit = remainder / DQN_YEARS_TO_S(1); Dqn_usize value = remainder / DQN_YEARS_TO_S(1);
remainder -= DQN_YEARS_TO_S(unit); remainder -= DQN_YEARS_TO_S(value);
if (unit) if (value)
Dqn_Str8Builder_AppendF(&builder, "%s%I64uyr", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64uyr", builder.string_size ? " " : "", value);
} }
if (type & Dqn_U64AgeUnit_Week) { if (unit & Dqn_U64AgeUnit_Week) {
Dqn_usize unit = remainder / DQN_WEEKS_TO_S(1); Dqn_usize value = remainder / DQN_WEEKS_TO_S(1);
remainder -= DQN_WEEKS_TO_S(unit); remainder -= DQN_WEEKS_TO_S(value);
if (unit) if (value)
Dqn_Str8Builder_AppendF(&builder, "%s%I64uw", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64uw", builder.string_size ? " " : "", value);
} }
if (type & Dqn_U64AgeUnit_Day) { if (unit & Dqn_U64AgeUnit_Day) {
Dqn_usize unit = remainder / DQN_DAYS_TO_S(1); Dqn_usize value = remainder / DQN_DAYS_TO_S(1);
remainder -= DQN_DAYS_TO_S(unit); remainder -= DQN_DAYS_TO_S(value);
if (unit) if (value)
Dqn_Str8Builder_AppendF(&builder, "%s%I64ud", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64ud", builder.string_size ? " " : "", value);
} }
if (type & Dqn_U64AgeUnit_Hr) { if (unit & Dqn_U64AgeUnit_Hr) {
Dqn_usize unit = remainder / DQN_HOURS_TO_S(1); Dqn_usize value = remainder / DQN_HOURS_TO_S(1);
remainder -= DQN_HOURS_TO_S(unit); remainder -= DQN_HOURS_TO_S(value);
if (unit) if (value)
Dqn_Str8Builder_AppendF(&builder, "%s%I64uh", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64uh", builder.string_size ? " " : "", value);
} }
if (type & Dqn_U64AgeUnit_Min) { if (unit & Dqn_U64AgeUnit_Min) {
Dqn_usize unit = remainder / DQN_MINS_TO_S(1); Dqn_usize value = remainder / DQN_MINS_TO_S(1);
remainder -= DQN_MINS_TO_S(unit); remainder -= DQN_MINS_TO_S(value);
if (unit) if (value)
Dqn_Str8Builder_AppendF(&builder, "%s%I64um", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64um", builder.string_size ? " " : "", value);
} }
if (type & Dqn_U64AgeUnit_Sec) { if (unit & Dqn_U64AgeUnit_Sec) {
Dqn_usize unit = remainder; Dqn_usize value = remainder;
Dqn_Str8Builder_AppendF(&builder, "%s%I64us", builder.string_size ? " " : "", unit); Dqn_Str8Builder_AddF(&builder, "%s%I64us", builder.string_size ? " " : "", value);
} }
result = Dqn_Str8Builder_Build(&builder, arena); result = Dqn_Str8Builder_Build(&builder, arena);
@ -841,10 +840,10 @@ DQN_API uint64_t Dqn_HexToU64(Dqn_Str8 hex)
DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags) DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
{ {
Dqn_Str8 prefix = {}; Dqn_Str8 prefix = {};
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) if ((flags & Dqn_HexU64Str8Flags_0xPrefix))
prefix = DQN_STR8("0x"); prefix = DQN_STR8("0x");
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; char const *fmt = (flags & Dqn_HexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size; Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size;
Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No); Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No);
@ -859,14 +858,14 @@ DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8(uint64_t number, uint32_t flags) DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8(uint64_t number, uint32_t flags)
{ {
Dqn_Str8 prefix = {}; Dqn_Str8 prefix = {};
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) if (flags & Dqn_HexU64Str8Flags_0xPrefix)
prefix = DQN_STR8("0x"); prefix = DQN_STR8("0x");
Dqn_U64HexStr8 result = {}; Dqn_U64HexStr8 result = {};
DQN_MEMCPY(result.data, prefix.data, prefix.size); DQN_MEMCPY(result.data, prefix.data, prefix.size);
result.size += DQN_CAST(int8_t) prefix.size; result.size += DQN_CAST(int8_t) prefix.size;
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; char const *fmt = (flags & Dqn_HexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number); int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
result.size += DQN_CAST(uint8_t) size; result.size += DQN_CAST(uint8_t) size;
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data)); DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data));
@ -905,9 +904,9 @@ DQN_API Dqn_Str8 Dqn_BytesToHex(Dqn_Arena *arena, void const *src, Dqn_usize siz
if (!src || size <= 0) if (!src || size <= 0)
return result; return result;
result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No); result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No);
result.data[result.size - 1] = 0; result.data[result.size] = 0;
bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size); bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size);
DQN_ASSERT(converted); DQN_ASSERT(converted);
return result; return result;
} }
@ -1029,10 +1028,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
// NOTE: Setup the allocation table with allocation tracking turned off on // NOTE: Setup the allocation table with allocation tracking turned off on
// the arena we're using to initialise the table. // the arena we're using to initialise the table.
result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack; result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096); result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096, Dqn_DSMapFlags_Nil);
#endif #endif
result->arena = Dqn_Arena_InitSize(0, 0, Dqn_ArenaFlag_AllocCanLeak); result->arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_KILOBYTES(64), Dqn_ArenaFlag_AllocCanLeak);
result->pool = Dqn_ChunkPool_Init(&result->arena, /*align*/ 0); result->pool = Dqn_ChunkPool_Init(&result->arena, /*align*/ 0);
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool); Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool);
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->arena, "Dqn Library"); Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->arena, "Dqn Library");
@ -1041,47 +1040,49 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->alloc_table_arena, "Dqn Allocation Table"); Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->alloc_table_arena, "Dqn Allocation Table");
#endif #endif
// NOTE: Initialise scratch arenas which allocate memory and will be // NOTE: Initialise tmem arenas which allocate memory and will be
// recorded to the now initialised allocation table. The initialisation // recorded to the now initialised allocation table. The initialisation
// of scratch memory may request scratch memory itself in leak tracing mode. // of tmem memory may request tmem memory itself in leak tracing mode.
// This is supported as the scratch arenas defer allocation tracking until // This is supported as the tmem arenas defer allocation tracking until
// initialisation is done. // initialisation is done.
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLS_Init(&result->tls);
Dqn_OS_ThreadSetTLS(&result->tls);
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
// NOTE: END IMPORTANT ORDER OF STATEMENTS ///////////////////////////////////////////////////// // NOTE: END IMPORTANT ORDER OF STATEMENTS /////////////////////////////////////////////////////
result->exe_dir = Dqn_OS_EXEDir(&result->arena); result->exe_dir = Dqn_OS_EXEDir(&result->arena);
// NOTE: Print out init features /////////////////////////////////////////////////////////////// // NOTE: Print out init features ///////////////////////////////////////////////////////////////
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = {};
builder.arena = scratch.arena; builder.arena = tmem.arena;
if (on_init & Dqn_LibraryOnInit_LogLibFeatures) { if (on_init & Dqn_LibraryOnInit_LogLibFeatures) {
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n")); Dqn_Str8Builder_AddRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
Dqn_f64 page_size_kib = result->os_page_size / 1024.0; Dqn_f64 page_size_kib = result->os_page_size / 1024.0;
Dqn_f64 alloc_granularity_kib = result->os_alloc_granularity / 1024.0; Dqn_f64 alloc_granularity_kib = result->os_alloc_granularity / 1024.0;
Dqn_Str8Builder_AppendF( Dqn_Str8Builder_AddF(
&builder, " OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n", page_size_kib, alloc_granularity_kib); &builder, " OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n", page_size_kib, alloc_granularity_kib);
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) #if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
if (DQN_ASAN_POISON) { if (DQN_ASAN_POISON) {
Dqn_Str8Builder_AppendF( Dqn_Str8Builder_AddF(
&builder, " ASAN manual poisoning%s\n", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : ""); &builder, " ASAN manual poisoning%s\n", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
Dqn_Str8Builder_AppendF(&builder, " ASAN poison guard size: %u\n", DQN_ASAN_POISON_GUARD_SIZE); Dqn_Str8Builder_AddF(&builder, " ASAN poison guard size: %u\n", DQN_ASAN_POISON_GUARD_SIZE);
} }
#endif #endif
#if defined(DQN_LEAK_TRACKING) #if defined(DQN_LEAK_TRACKING)
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Allocation leak tracing\n")); Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Allocation leak tracing\n"));
#endif #endif
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" TSC profiler available\n")); Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" TSC profiler available\n"));
#endif #endif
#if defined(DQN_USE_STD_PRINTF) #if defined(DQN_USE_STD_PRINTF)
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stdio's printf functions\n")); Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Using stdio's printf functions\n"));
#else #else
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stb_sprintf functions\n")); Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Using stb_sprintf functions\n"));
#endif #endif
// TODO(doyle): Add stacktrace feature log // TODO(doyle): Add stacktrace feature log
@ -1090,10 +1091,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
if (on_init & Dqn_LibraryOnInit_LogCPUFeatures) { if (on_init & Dqn_LibraryOnInit_LogCPUFeatures) {
Dqn_CPUReport const *report = &result->cpu_report; Dqn_CPUReport const *report = &result->cpu_report;
Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1)); Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1));
Dqn_Str8Builder_AppendF(&builder, Dqn_Str8Builder_AddF(&builder,
" CPU '%.*s' from '%s' detected:\n", " CPU '%.*s' from '%s' detected:\n",
DQN_STR_FMT(brand), DQN_STR_FMT(brand),
report->vendor); report->vendor);
Dqn_usize longest_feature_name = 0; Dqn_usize longest_feature_name = 0;
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) { DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
@ -1104,16 +1105,16 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) { DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index]; Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index];
bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value); bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value);
Dqn_Str8Builder_AppendF(&builder, Dqn_Str8Builder_AddF(&builder,
" %.*s:%*s%s\n", " %.*s:%*s%s\n",
DQN_STR_FMT(feature_decl.label), DQN_STR_FMT(feature_decl.label),
DQN_CAST(int)(longest_feature_name - feature_decl.label.size), DQN_CAST(int)(longest_feature_name - feature_decl.label.size),
"", "",
has_feature ? "available" : "not available"); has_feature ? "available" : "not available");
} }
} }
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena); Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, tmem.arena);
if (Dqn_Str8_HasData(info_log)) if (Dqn_Str8_HasData(info_log))
Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log)); Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log));
return result; return result;
@ -1121,8 +1122,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
DQN_API void Dqn_Library_SetPointer(Dqn_Library *library) DQN_API void Dqn_Library_SetPointer(Dqn_Library *library)
{ {
if (library) if (library) {
g_dqn_library = library; g_dqn_library = library;
Dqn_OS_ThreadSetTLS(&library->tls);
}
} }
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
@ -1216,21 +1219,42 @@ DQN_API Dqn_Arena *Dqn_Library_AllocArenaF(Dqn_usize reserve, Dqn_usize commit,
return result; return result;
} }
#if !defined(DQN_NO_PROFILER) DQN_API bool Dqn_Library_EraseArena(Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena)
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
{ {
zone = Dqn_Profiler_BeginZoneWithIndex(name, anchor_index); Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
bool result = Dqn_ArenaCatalog_Erase(catalog, arena, free_arena);
return result;
} }
Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope() #if !defined(DQN_NO_PROFILER)
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
DQN_API Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
{
zone = Dqn_Profiler_BeginZoneAtIndex(name, anchor_index);
}
DQN_API Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope()
{ {
Dqn_Profiler_EndZone(zone); Dqn_Profiler_EndZone(zone);
} }
Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index) DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_ReadBuffer()
{ {
Dqn_ProfilerAnchor *anchor = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back) + anchor_index; Dqn_ProfilerAnchor *result = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
return result;
}
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_WriteBuffer()
{
Dqn_ProfilerAnchor *result = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
return result;
}
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneAtIndex(Dqn_Str8 name, uint16_t anchor_index)
{
Dqn_ProfilerAnchor *anchor = Dqn_Profiler_WriteBuffer() + anchor_index;
if (Dqn_Str8_HasData(anchor->name))
DQN_ASSERTF(name == anchor->name, "Potentially overwriting a zone by accident?");
anchor->name = name; anchor->name = name;
Dqn_ProfilerZone result = {}; Dqn_ProfilerZone result = {};
result.begin_tsc = Dqn_CPU_TSC(); result.begin_tsc = Dqn_CPU_TSC();
@ -1241,10 +1265,10 @@ Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_
return result; return result;
} }
void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone) DQN_API void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
{ {
uint64_t elapsed_tsc = Dqn_CPU_TSC() - zone.begin_tsc; uint64_t elapsed_tsc = Dqn_CPU_TSC() - zone.begin_tsc;
Dqn_ProfilerAnchor *anchor_buffer = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); Dqn_ProfilerAnchor *anchor_buffer = Dqn_Profiler_WriteBuffer();
Dqn_ProfilerAnchor *anchor = anchor_buffer + zone.anchor_index; Dqn_ProfilerAnchor *anchor = anchor_buffer + zone.anchor_index;
anchor->hit_count++; anchor->hit_count++;
@ -1256,7 +1280,7 @@ void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
g_dqn_library->profiler->parent_zone = zone.parent_zone; g_dqn_library->profiler->parent_zone = zone.parent_zone;
} }
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer) DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
{ {
uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1; uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1;
uint8_t anchor_buffer = uint8_t anchor_buffer =
@ -1265,7 +1289,7 @@ Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
return result; return result;
} }
void Dqn_Profiler_SwapAnchorBuffer() DQN_API void Dqn_Profiler_SwapAnchorBuffer()
{ {
g_dqn_library->profiler->active_anchor_buffer++; g_dqn_library->profiler->active_anchor_buffer++;
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
@ -1274,9 +1298,9 @@ void Dqn_Profiler_SwapAnchorBuffer()
DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0])); DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0]));
} }
void Dqn_Profiler_Dump(uint64_t tsc_per_second) DQN_API void Dqn_Profiler_Dump(uint64_t tsc_per_second)
{ {
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); Dqn_ProfilerAnchor *anchors = Dqn_Profiler_ReadBuffer();
for (size_t anchor_index = 1; anchor_index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) { for (size_t anchor_index = 1; anchor_index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) {
Dqn_ProfilerAnchor const *anchor = anchors + anchor_index; Dqn_ProfilerAnchor const *anchor = anchors + anchor_index;
if (!anchor->hit_count) if (!anchor->hit_count)
@ -1343,8 +1367,8 @@ DQN_API bool Dqn_OS_JobQueueSPMCAdd(Dqn_JobQueueSPMC *queue, Dqn_Job job)
DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread) DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
{ {
Dqn_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context; Dqn_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context;
uint32_t const pot_mask = DQN_ARRAY_UCOUNT(queue->jobs) - 1; uint32_t const pot_mask = DQN_ARRAY_UCOUNT(queue->jobs) - 1;
static_assert(DQN_ARRAY_UCOUNT(queue->jobs) == DQN_ARRAY_UCOUNT(queue->complete_queue), "PoT mask is used to mask access to both arrays"); static_assert(DQN_ARRAY_UCOUNT(queue->jobs) == DQN_ARRAY_UCOUNT(queue->complete_queue), "PoT mask is used to mask access to both arrays");
for (;;) { for (;;) {
@ -1360,8 +1384,9 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
queue->read_index += 1; queue->read_index += 1;
Dqn_OS_MutexUnlock(&queue->mutex); Dqn_OS_MutexUnlock(&queue->mutex);
job.func(job.user_context); job.elapsed_tsc -= Dqn_CPU_TSC();
Dqn_Arena_Deinit(job.arena); job.func(thread, job.user_context);
job.elapsed_tsc += Dqn_CPU_TSC();
if (job.add_to_completion_queue) { if (job.add_to_completion_queue) {
Dqn_OS_SemaphoreWait(&queue->complete_queue_write_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT); Dqn_OS_SemaphoreWait(&queue->complete_queue_write_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
@ -1371,9 +1396,15 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
Dqn_OS_SemaphoreIncrement(&queue->complete_queue_write_semaphore, 1); Dqn_OS_SemaphoreIncrement(&queue->complete_queue_write_semaphore, 1);
} }
// NOTE: Update finish counter
Dqn_OS_MutexLock(&queue->mutex); Dqn_OS_MutexLock(&queue->mutex);
queue->finish_index += 1; queue->finish_index += 1;
if (queue->finish_index == queue->write_index && queue->threads_waiting_for_completion) {
// NOTE: If all jobs are finished and we have another thread who is
// blocked via `WaitForCompletion` for this job queue, we will go
// release the semaphore to wake them all up.
bool all_jobs_finished = queue->finish_index == queue->write_index;
if (all_jobs_finished && queue->threads_waiting_for_completion) {
Dqn_OS_SemaphoreIncrement(&queue->wait_for_completion_semaphore, Dqn_OS_SemaphoreIncrement(&queue->wait_for_completion_semaphore,
queue->threads_waiting_for_completion); queue->threads_waiting_for_completion);
queue->threads_waiting_for_completion = 0; queue->threads_waiting_for_completion = 0;

View File

@ -64,8 +64,7 @@ enum Dqn_BinarySearchType
{ {
// Index of the match. If no match is found, found is set to false and the // Index of the match. If no match is found, found is set to false and the
// index is set to the index where the match should be inserted/exist, if // index is set to the index where the match should be inserted/exist, if
// it were in the array (in practice this turns out to be the first or // it were in the array
// [last index + 1] of the array).
Dqn_BinarySearchType_Match, Dqn_BinarySearchType_Match,
// Index of the first element in the array that is `element >= find`. If no such // Index of the first element in the array that is `element >= find`. If no such
@ -97,6 +96,9 @@ struct Dqn_BinarySearchResult
Dqn_usize index; Dqn_usize index;
}; };
template <typename T>
using Dqn_QSortLessThanProc = bool(T const &a, T const &b, void *user_context);
// NOTE: [$MISC] Misc ////////////////////////////////////////////////////////////////////////////// // NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
struct Dqn_U64Str8 struct Dqn_U64Str8
{ {
@ -122,7 +124,8 @@ struct Dqn_U64ByteSize
Dqn_f64 bytes; Dqn_f64 bytes;
}; };
enum Dqn_U64AgeUnit typedef uint32_t Dqn_U64AgeUnit;
enum Dqn_U64AgeUnit_
{ {
Dqn_U64AgeUnit_Sec = 1 << 0, Dqn_U64AgeUnit_Sec = 1 << 0,
Dqn_U64AgeUnit_Min = 1 << 1, Dqn_U64AgeUnit_Min = 1 << 1,
@ -142,8 +145,9 @@ struct Dqn_U64HexStr8
enum Dqn_U64HexStr8Flags enum Dqn_U64HexStr8Flags
{ {
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string Dqn_HexU64Str8Flags_Nil = 0,
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex Dqn_HexU64Str8Flags_0xPrefix = 1 << 0, /// Add the '0x' prefix from the string
Dqn_HexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
}; };
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
@ -182,12 +186,12 @@ struct Dqn_ProfilerZoneScope
~Dqn_ProfilerZoneScope(); ~Dqn_ProfilerZoneScope();
Dqn_ProfilerZone zone; Dqn_ProfilerZone zone;
}; };
#define Dqn_Profiler_ZoneScopeWithIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index) #define Dqn_Profiler_ZoneScopeAtIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index)
#define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeWithIndex(name, __COUNTER__ + 1) #define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeAtIndex(name, __COUNTER__ + 1)
#endif #endif
#define Dqn_Profiler_ZoneBlockIndex(name, index) \ #define Dqn_Profiler_ZoneBlockIndex(name, index) \
for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneWithIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \ for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneAtIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \
DQN_UNIQUE_NAME(dummy__).begin_tsc == 0; \ DQN_UNIQUE_NAME(dummy__).begin_tsc == 0; \
Dqn_Profiler_EndZone(DQN_UNIQUE_NAME(profile_zone__)), DQN_UNIQUE_NAME(dummy__).begin_tsc = 1) Dqn_Profiler_EndZone(DQN_UNIQUE_NAME(profile_zone__)), DQN_UNIQUE_NAME(dummy__).begin_tsc = 1)
@ -208,13 +212,14 @@ struct Dqn_Profiler
#endif // !defined(DQN_NO_PROFILER) #endif // !defined(DQN_NO_PROFILER)
// NOTE: [$JOBQ] Dqn_JobQueue /////////////////////////////////////////////////////////////////////// // NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
typedef void (Dqn_JobQueueFunc)(void *user_context); typedef void (Dqn_JobQueueFunc)(Dqn_OSThread *thread, void *user_context);
struct Dqn_Job struct Dqn_Job
{ {
bool add_to_completion_queue; Dqn_JobQueueFunc *func; // The function to invoke for the job
Dqn_Arena *arena; void *user_context; // Pointer user can set to use in their `job_func`
Dqn_JobQueueFunc *func; uint64_t elapsed_tsc;
void *user_context; uint16_t user_tag; // Arbitrary value the user can set to identiy the type of `user_context` this job has
bool add_to_completion_queue; // When true, on job completion, job must be dequeued from the completion queue via `GetFinishedJobs`
}; };
#if !defined(DQN_JOB_QUEUE_SPMC_SIZE) #if !defined(DQN_JOB_QUEUE_SPMC_SIZE)
@ -248,13 +253,14 @@ struct Dqn_Library
{ {
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
Dqn_TicketMutex lib_mutex; Dqn_TicketMutex lib_mutex;
Dqn_TicketMutex thread_context_init_mutex;
Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Str8 exe_dir; // The directory of the current executable
Dqn_Arena arena; Dqn_Arena arena;
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
Dqn_ArenaCatalog arena_catalog; Dqn_ArenaCatalog arena_catalog;
bool slow_verification_checks; // Enable expensive library verification checks bool slow_verification_checks; // Enable expensive library verification checks
Dqn_CPUReport cpu_report; Dqn_CPUReport cpu_report;
Dqn_TLS tls; // Thread local storage state for the main thread.
// NOTE: Logging /////////////////////////////////////////////////////////////////////////////// // NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
void * log_user_data; void * log_user_data;
@ -262,22 +268,27 @@ struct Dqn_Library
Dqn_OSFile log_file; // TODO(dqn): Hmmm, how should we do this... ? Dqn_OSFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
bool log_no_colour; // Disable colours in the logging output bool log_no_colour; // Disable colours in the logging output
// NOTE: Leak Tracing ////////////////////////////////////////////////////////////////////////// // NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
#if defined(DQN_LEAK_TRACKING) #if defined(DQN_LEAK_TRACKING)
Dqn_DSMap<Dqn_DebugAlloc> alloc_table; Dqn_DSMap<Dqn_DebugAlloc> alloc_table;
Dqn_TicketMutex alloc_table_mutex; Dqn_TicketMutex alloc_table_mutex;
Dqn_Arena alloc_table_arena; Dqn_Arena alloc_table_arena;
#endif #endif
// NOTE: Win32 ///////////////////////////////////////////////////////////////////////////////// // NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
LARGE_INTEGER win32_qpc_frequency; LARGE_INTEGER win32_qpc_frequency;
Dqn_TicketMutex win32_bcrypt_rng_mutex; Dqn_TicketMutex win32_bcrypt_rng_mutex;
void * win32_bcrypt_rng_handle; void * win32_bcrypt_rng_handle;
#endif #endif
bool win32_sym_initialised; bool win32_sym_initialised;
// NOTE: OS //////////////////////////////////////////////////////////////////////////////////// // NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
uint32_t os_page_size; uint32_t os_page_size;
uint32_t os_alloc_granularity; uint32_t os_alloc_granularity;
// NOTE: Profiler ////////////////////////////////////////////////////////////////////////////// // NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
Dqn_Profiler *profiler; Dqn_Profiler *profiler;
@ -354,6 +365,13 @@ template <typename T> Dqn_BinarySearchResult Dqn_BinarySearch (T
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match, Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan); Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
// NOTE: [$QSOR] Dqn_QSort /////////////////////////////////////////////////////////////////////////
template <typename T> bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs);
template <typename T> void Dqn_QSort (T *array,
Dqn_usize array_size,
void *user_context,
Dqn_QSortLessThanProc<T> less_than = Dqn_QSort_DefaultLessThan);
// NOTE: [$BITS] Dqn_Bit /////////////////////////////////////////////////////////////////////////// // NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize *flags, Dqn_usize bitfield); DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize *flags, Dqn_usize bitfield);
DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield); DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield);
@ -426,23 +444,28 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t v
DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize (uint64_t bytes, Dqn_U64ByteSizeType type); DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize (uint64_t bytes, Dqn_U64ByteSizeType type);
DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type); DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type);
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type); DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type); DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_U64AgeUnit unit);
DQN_API uint64_t Dqn_HexToU64 (Dqn_Str8 hex); DQN_API uint64_t Dqn_HexToU64 (Dqn_Str8 hex);
DQN_API Dqn_Str8 Dqn_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags); DQN_API Dqn_Str8 Dqn_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8 (uint64_t number, uint32_t flags); DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8 (uint64_t number, uint32_t flags);
DQN_API bool Dqn_BytesToHexPtr (Dqn_Arena *arena, void const *src, Dqn_usize src_size, char *dest); DQN_API bool Dqn_BytesToHexPtr (void const *src, Dqn_usize src_size, char *dest);
DQN_API Dqn_Str8 Dqn_BytesToHex (Dqn_Arena *arena, void const *src, Dqn_usize size); DQN_API Dqn_Str8 Dqn_BytesToHex (Dqn_Arena *arena, void const *src, Dqn_usize size);
#define Dqn_BytesToHex_TLS(...) Dqn_BytesToHex(Dqn_TLS_TopArena(), __VA_ARGS__)
DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek); DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek); DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked (Dqn_Arena *arena, Dqn_Str8 hex); DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked (Dqn_Arena *arena, Dqn_Str8 hex);
#define Dqn_HexToBytesUnchecked_TLS(...) Dqn_HexToBytesUnchecked(Dqn_TLS_TopArena(), __VA_ARGS__)
DQN_API Dqn_Str8 Dqn_HexToBytes (Dqn_Arena *arena, Dqn_Str8 hex); DQN_API Dqn_Str8 Dqn_HexToBytes (Dqn_Arena *arena, Dqn_Str8 hex);
#define Dqn_HexToBytes_TLS(...) Dqn_HexToBytes(Dqn_TLS_TopArena(), __VA_ARGS__)
// NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// // NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1) DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_ReadBuffer ();
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index); DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_WriteBuffer ();
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneAtIndex(DQN_STR8(name), __COUNTER__ + 1)
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneAtIndex (Dqn_Str8 name, uint16_t anchor_index);
DQN_API void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone); DQN_API void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer); DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
DQN_API void Dqn_Profiler_SwapAnchorBuffer (); DQN_API void Dqn_Profiler_SwapAnchorBuffer ();
@ -465,6 +488,7 @@ DQN_API void Dqn_Library_SetProfiler (Dqn_Profil
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data); DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path); DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path);
DQN_API Dqn_Arena * Dqn_Library_AllocArenaF (Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...); DQN_API Dqn_Arena * Dqn_Library_AllocArenaF (Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...);
DQN_API bool Dqn_Library_EraseArena (Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena);
// NOTE: [$BSEA] Dqn_BinarySearch ////////////////////////////////////////////////////////////////// // NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
@ -482,7 +506,7 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
Dqn_BinarySearchLessThanProc<T> less_than) Dqn_BinarySearchLessThanProc<T> less_than)
{ {
Dqn_BinarySearchResult result = {}; Dqn_BinarySearchResult result = {};
if (!array || array_size <= 0) if (!array || array_size <= 0 || !less_than)
return result; return result;
T const *end = array + array_size; T const *end = array + array_size;
@ -519,3 +543,73 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
return result; return result;
} }
// NOTE: [$QSOR] Dqn_QSort /////////////////////////////////////////////////////////////////////////
template <typename T>
bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context)
{
(void)user_context;
bool result = lhs < rhs;
return result;
}
template <typename T>
void Dqn_QSort(T *array, Dqn_usize array_size, void *user_context, Dqn_QSortLessThanProc<T> less_than)
{
if (!array || array_size <= 1 || !less_than)
return;
// NOTE: Insertion Sort, under 24->32 is an optimal amount /////////////////////////////////////
const Dqn_usize QSORT_THRESHOLD = 24;
if (array_size < QSORT_THRESHOLD) {
for (Dqn_usize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) {
for (Dqn_usize index = 0; index < item_to_insert_index; index++) {
if (!less_than(array[index], array[item_to_insert_index], user_context)) {
T item_to_insert = array[item_to_insert_index];
for (Dqn_usize i = item_to_insert_index; i > index; i--)
array[i] = array[i - 1];
array[index] = item_to_insert;
break;
}
}
}
return;
}
// NOTE: Quick sort, under 24->32 is an optimal amount /////////////////////////////////////////
Dqn_usize last_index = array_size - 1;
Dqn_usize pivot_index = array_size / 2;
Dqn_usize partition_index = 0;
Dqn_usize start_index = 0;
// Swap pivot with last index, so pivot is always at the end of the array.
// This makes logic much simpler.
DQN_SWAP(array[last_index], array[pivot_index]);
pivot_index = last_index;
// 4^, 8, 7, 5, 2, 3, 6
if (less_than(array[start_index], array[pivot_index], user_context))
partition_index++;
start_index++;
// 4, |8, 7, 5^, 2, 3, 6*
// 4, 5, |7, 8, 2^, 3, 6*
// 4, 5, 2, |8, 7, ^3, 6*
// 4, 5, 2, 3, |7, 8, ^6*
for (Dqn_usize index = start_index; index < last_index; index++) {
if (less_than(array[index], array[pivot_index], user_context)) {
DQN_SWAP(array[partition_index], array[index]);
partition_index++;
}
}
// Move pivot to right of partition
// 4, 5, 2, 3, |6, 8, ^7*
DQN_SWAP(array[partition_index], array[pivot_index]);
Dqn_QSort(array, partition_index, user_context, less_than);
// Skip the value at partion index since that is guaranteed to be sorted.
// 4, 5, 2, 3, (x), 8, 7
Dqn_usize one_after_partition_index = partition_index + 1;
Dqn_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than);
}

View File

@ -2,7 +2,7 @@
#include "dqn.h" #include "dqn.h"
#if !defined(SHEREDOM_JSON_H_INCLUDED) #if !defined(SHEREDOM_JSON_H_INCLUDED)
#error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file #error Sheredom json.h (github.com/sheredom/json.h) must be included before this file
#endif #endif
// NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// // NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////

View File

@ -1212,10 +1212,10 @@ DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat)
Dqn_FStr8<256> result = {}; Dqn_FStr8<256> result = {};
for (int row = 0; row < 4; row++) { for (int row = 0; row < 4; row++) {
for (int it = 0; it < 4; it++) { for (int it = 0; it < 4; it++) {
if (it == 0) Dqn_FStr8_Append(&result, DQN_STR8("|")); if (it == 0) Dqn_FStr8_Add(&result, DQN_STR8("|"));
Dqn_FStr8_AppendF(&result, "%.5f", mat.columns[it][row]); Dqn_FStr8_AddF(&result, "%.5f", mat.columns[it][row]);
if (it != 3) Dqn_FStr8_Append(&result, DQN_STR8(", ")); if (it != 3) Dqn_FStr8_Add(&result, DQN_STR8(", "));
else Dqn_FStr8_Append(&result, DQN_STR8("|\n")); else Dqn_FStr8_Add(&result, DQN_STR8("|\n"));
} }
} }

View File

@ -75,8 +75,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
Dqn_Str8 result = {}; Dqn_Str8 result = {};
if (!arena) if (!arena)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena); Dqn_Str8 exe_path = Dqn_OS_EXEPath(tmem.arena);
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators)); Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
result = Dqn_Str8_Copy(arena, split.lhs); result = Dqn_Str8_Copy(arena, split.lhs);
@ -172,6 +172,14 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_f
#if !defined(DQN_NO_OS_FILE_API) #if !defined(DQN_NO_OS_FILE_API)
// NOTE: [$FILE] Dqn_OSPathInfo/File /////////////////////////////////////////////////////////////// // NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_FileIsOlderThan(Dqn_Str8 file, Dqn_Str8 check_against)
{
Dqn_OSPathInfo file_info = Dqn_OS_PathInfo(file);
Dqn_OSPathInfo check_against_info = Dqn_OS_PathInfo(check_against);
bool result = !file_info.exists || file_info.last_write_time_in_s < check_against_info.last_write_time_in_s;
return result;
}
DQN_API bool Dqn_OS_FileWrite(Dqn_OSFile *file, Dqn_Str8 buffer, Dqn_ErrorSink *error) DQN_API bool Dqn_OS_FileWrite(Dqn_OSFile *file, Dqn_Str8 buffer, Dqn_ErrorSink *error)
{ {
bool result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error); bool result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error);
@ -183,8 +191,8 @@ DQN_API bool Dqn_OS_FileWriteFV(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_
bool result = false; bool result = false;
if (!file || !fmt) if (!file || !fmt)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error); result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error);
return result; return result;
} }
@ -199,7 +207,7 @@ DQN_API bool Dqn_OS_FileWriteF(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_A
} }
// NOTE: R/W Entire File /////////////////////////////////////////////////////////////////////////// // NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error) DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Arena *arena, Dqn_Str8 path, Dqn_ErrorSink *error)
{ {
Dqn_Str8 result = {}; Dqn_Str8 result = {};
if (!arena) if (!arena)
@ -215,8 +223,8 @@ DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena); Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
result = Dqn_Str8_Alloc(arena, path_info.size, Dqn_ZeroMem_No); result = Dqn_Str8_Alloc(arena, path_info.size, Dqn_ZeroMem_No);
if (!Dqn_Str8_HasData(result)) { if (!Dqn_Str8_HasData(result)) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, path_info.size, Dqn_U64ByteSizeType_Auto); Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(tmem.arena, path_info.size, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(path)); Dqn_ErrorSink_MakeF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(path));
Dqn_Arena_TempMemEnd(temp_mem); Dqn_Arena_TempMemEnd(temp_mem);
result = {}; result = {};
@ -244,8 +252,8 @@ DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *erro
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args) DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
bool result = Dqn_OS_WriteAll(file_path, buffer, error); bool result = Dqn_OS_WriteAll(file_path, buffer, error);
return result; return result;
} }
@ -261,8 +269,8 @@ DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error) DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path)); Dqn_Str8 tmp_path = Dqn_Str8_InitF(tmem.arena, "%.*s.tmp", DQN_STR_FMT(path));
if (!Dqn_OS_WriteAll(tmp_path, buffer, error)) if (!Dqn_OS_WriteAll(tmp_path, buffer, error))
return false; return false;
if (!Dqn_OS_CopyFile(tmp_path, path, true /*overwrite*/, error)) if (!Dqn_OS_CopyFile(tmp_path, path, true /*overwrite*/, error))
@ -274,8 +282,8 @@ DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args) DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
bool result = Dqn_OS_WriteAllSafe(path, buffer, error); bool result = Dqn_OS_WriteAllSafe(path, buffer, error);
return result; return result;
} }
@ -375,7 +383,7 @@ DQN_API bool Dqn_OS_PathPop(Dqn_OSPath *fs_path)
return true; return true;
} }
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separator) DQN_API Dqn_Str8 Dqn_OS_PathTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separator)
{ {
Dqn_OSPath fs_path = {}; Dqn_OSPath fs_path = {};
Dqn_OS_PathAddRef(arena, &fs_path, path); Dqn_OS_PathAddRef(arena, &fs_path, path);
@ -383,31 +391,31 @@ DQN_API Dqn_Str8 Dqn_OS_PathConvertTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF(Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...) DQN_API Dqn_Str8 Dqn_OS_PathToF(Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 path = Dqn_Str8_InitFV(tmem.arena, fmt, args);
va_end(args); va_end(args);
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, path_separator); Dqn_Str8 result = Dqn_OS_PathTo(arena, path, path_separator);
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_OS_PathConvert(Dqn_Arena *arena, Dqn_Str8 path) DQN_API Dqn_Str8 Dqn_OS_Path(Dqn_Arena *arena, Dqn_Str8 path)
{ {
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, Dqn_OSPathSeperatorString); Dqn_Str8 result = Dqn_OS_PathTo(arena, path, Dqn_OSPathSeperatorString);
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_OS_PathConvertF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...) DQN_API Dqn_Str8 Dqn_OS_PathF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args); Dqn_Str8 path = Dqn_Str8_InitFV(tmem.arena, fmt, args);
va_end(args); va_end(args);
Dqn_Str8 result = Dqn_OS_PathConvert(arena, path); Dqn_Str8 result = Dqn_OS_Path(arena, path);
return result; return result;
} }
@ -480,6 +488,23 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_St
return result; return result;
} }
// NOTE: [$THRD] Dqn_OSThread //////////////////////////////////////////////////////////////////////
DQN_THREAD_LOCAL Dqn_TLS *g_dqn_os_thread_tls;
static void Dqn_OS_ThreadExecute_(void *user_context)
{
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context;
Dqn_TLS_Init(&thread->tls);
Dqn_OS_ThreadSetTLS(&thread->tls);
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
thread->func(thread);
}
DQN_API void Dqn_OS_ThreadSetTLS(Dqn_TLS *tls)
{
g_dqn_os_thread_tls = tls;
}
// NOTE: [$HTTP] Dqn_OSHttp //////////////////////////////////////////////////////////////////////// // NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response) DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
{ {
@ -490,9 +515,9 @@ DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest(Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers) DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest(Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers)
{ {
// TODO(doyle): Revise the memory allocation and its lifetime // TODO(doyle): Revise the memory allocation and its lifetime
Dqn_OSHttpResponse result = {}; Dqn_OSHttpResponse result = {};
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
result.scratch_arena = scratch.arena; result.tmem_arena = tmem.arena;
Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
Dqn_OS_HttpRequestWait(&result); Dqn_OS_HttpRequestWait(&result);

View File

@ -122,6 +122,13 @@ struct Dqn_OSPathInfo
uint64_t size; uint64_t size;
}; };
struct Dqn_OS_DirIterator
{
void *handle;
Dqn_Str8 file_name;
char buffer[512];
};
// NOTE: R/W Stream API //////////////////////////////////////////////////////////////////////////// // NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
struct Dqn_OSFile struct Dqn_OSFile
{ {
@ -231,6 +238,7 @@ typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*);
struct Dqn_OSThread struct Dqn_OSThread
{ {
Dqn_TLS tls;
void *handle; void *handle;
uint64_t thread_id; uint64_t thread_id;
void *user_context; void *user_context;
@ -262,7 +270,7 @@ struct Dqn_OSHttpResponse
// Synchronous HTTP response uses the TLS scratch arena whereas async // Synchronous HTTP response uses the TLS scratch arena whereas async
// calls use their own dedicated arena. // calls use their own dedicated arena.
Dqn_Arena tmp_arena; Dqn_Arena tmp_arena;
Dqn_Arena *scratch_arena; Dqn_Arena *tmem_arena;
Dqn_Str8Builder builder; Dqn_Str8Builder builder;
Dqn_OSSemaphore on_complete_semaphore; Dqn_OSSemaphore on_complete_semaphore;
@ -295,6 +303,7 @@ DQN_API bool Dqn_OS_DateIsValid (Dqn_OSDateTime dat
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size); DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena); DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena);
DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena *arena); DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena *arena);
#define Dqn_OS_EXEDir_TLS() Dqn_OS_EXEDir(Dqn_TLS_TopArena())
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds); DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
// NOTE: Counters ////////////////////////////////////////////////////////////////////////////////// // NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
DQN_API uint64_t Dqn_OS_PerfCounterNow (); DQN_API uint64_t Dqn_OS_PerfCounterNow ();
@ -313,13 +322,15 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_
#if !defined(DQN_NO_OS_FILE_API) #if !defined(DQN_NO_OS_FILE_API)
// NOTE: File system paths ///////////////////////////////////////////////////////////////////////// // NOTE: File system paths /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path); DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path); DQN_API bool Dqn_OS_FileIsOlderThan(Dqn_Str8 file, Dqn_Str8 check_against);
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path); DQN_API bool Dqn_OS_PathDelete (Dqn_Str8 path);
DQN_API bool Dqn_OS_CopyFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error); DQN_API bool Dqn_OS_FileExists (Dqn_Str8 path);
DQN_API bool Dqn_OS_MoveFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error); DQN_API bool Dqn_OS_CopyFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
DQN_API bool Dqn_OS_MakeDir (Dqn_Str8 path, Dqn_ErrorSink *error); DQN_API bool Dqn_OS_MoveFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path, Dqn_ErrorSink *error); DQN_API bool Dqn_OS_MakeDir (Dqn_Str8 path);
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path);
DQN_API bool Dqn_OS_DirIterate (Dqn_Str8 path, Dqn_OS_DirIterator *it);
// NOTE: R/W Stream API //////////////////////////////////////////////////////////////////////////// // NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSFile Dqn_OS_FileOpen (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error); DQN_API Dqn_OSFile Dqn_OS_FileOpen (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error);
@ -331,35 +342,45 @@ DQN_API bool Dqn_OS_FileWriteF (Dqn_OSFile *file, Dqn_Erro
DQN_API void Dqn_OS_FileClose (Dqn_OSFile *file); DQN_API void Dqn_OS_FileClose (Dqn_OSFile *file);
// NOTE: R/W Entire File /////////////////////////////////////////////////////////////////////////// // NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error); DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Arena *arena, Dqn_Str8 path, Dqn_ErrorSink *error);
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); #define Dqn_OS_ReadAll_TLS(...) Dqn_OS_ReadAll(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API bool Dqn_OS_WriteAllSafeFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
#endif // !defined(DQN_NO_OS_FILE_API) #endif // !defined(DQN_NO_OS_FILE_API)
// NOTE: File system paths ///////////////////////////////////////////////////////////////////////// // NOTE: File system paths /////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path); DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path); #define Dqn_OS_PathAddRef_TLS(...) Dqn_OS_PathAddRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path); #define Dqn_OS_PathAdd_TLS(...) Dqn_OS_PathAdd(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator); DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor); #define Dqn_OS_PathAddF_TLS(...) Dqn_OS_PathAddF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path);
DQN_API Dqn_Str8 Dqn_OS_PathConvert (Dqn_Arena *arena, Dqn_Str8 path); DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator (Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator);
DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); #define Dqn_OS_PathBuildWithSeperator_TLS(...) Dqn_OS_PathBuildWithSeperator(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_PathTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor);
#define Dqn_OS_PathTo_TLS(...) Dqn_OS_PathTo(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_PathToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...);
#define Dqn_OS_PathToF_TLS(...) Dqn_OS_PathToF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_Path (Dqn_Arena *arena, Dqn_Str8 path);
#define Dqn_OS_Path_TLS(...) Dqn_OS_Path(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_OS_PathF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
#define Dqn_OS_PathF_TLS(...) Dqn_OS_PathF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
#define Dqn_OS_PathBuildFwdSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("/")) #define Dqn_OS_PathBuildFwdSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("/"))
#define Dqn_OS_PathBuildBackSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("\\")) #define Dqn_OS_PathBuildBackSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("\\"))
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, Dqn_OSPathSeparatorString) #define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, Dqn_OSPathSeparatorString)
// NOTE: [$EXEC] Dqn_OSExec //////////////////////////////////////////////////////////////////////// // NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_Exit (int32_t exit_code); DQN_API void Dqn_OS_Exit (int32_t exit_code);
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle, Dqn_Arena *arena, Dqn_ErrorSink *error); DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle, Dqn_Arena *arena, Dqn_ErrorSink *error);
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error); DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error);
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena, Dqn_ErrorSink *error); DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena, Dqn_ErrorSink *error);
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena); DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena);
#define Dqn_OS_ExecOrAbort_TLS(...) Dqn_OS_ExecOrAbort(__VA_ARGS__, Dqn_TLS_TopArena())
// NOTE: [$SEMA] Dqn_OSSemaphore /////////////////////////////////////////////////////////////////// // NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_SEMAPHORE) #if !defined(DQN_NO_SEMAPHORE)
@ -381,6 +402,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex);
DQN_API bool Dqn_OS_ThreadInit (Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context); DQN_API bool Dqn_OS_ThreadInit (Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context);
DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread thread); DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread thread);
DQN_API uint32_t Dqn_OS_ThreadID (); DQN_API uint32_t Dqn_OS_ThreadID ();
DQN_API void Dqn_OS_ThreadSetTLS(Dqn_TLS *tls);
#endif // !defined(DQN_NO_THREAD) #endif // !defined(DQN_NO_THREAD)
// NOTE: [$HTTP] Dqn_OSHttp //////////////////////////////////////////////////////////////////////// // NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "dqn.h" #include "dqn.h"
#include <dirent.h> // readdir, opendir, closedir
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -271,6 +273,12 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
// shoddily deal with this. // shoddily deal with this.
result.create_time_in_s = result.create_time_in_s =
DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s); DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s);
if (S_ISDIR(file_stat.st_mode)) {
result.type = Dqn_OSPathInfoType_Directory;
} else if (S_ISREG(file_stat.st_mode)) {
result.type = Dqn_OSPathInfoType_File;
}
} }
return result; return result;
} }
@ -350,11 +358,11 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
result = (bytes_written == stat_existing.st_size); result = (bytes_written == stat_existing.st_size);
if (!result) { if (!result) {
int error_code = errno; int error_code = errno;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 file_size_str8 = Dqn_Str8 file_size_str8 =
Dqn_U64ToByteSizeStr8(scratch.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto); Dqn_U64ToByteSizeStr8(tmem.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto);
Dqn_Str8 bytes_written_str8 = Dqn_Str8 bytes_written_str8 =
Dqn_U64ToByteSizeStr8(scratch.arena, bytes_written, Dqn_U64ByteSizeType_Auto); Dqn_U64ToByteSizeStr8(tmem.arena, bytes_written, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
error_code, error_code,
"Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file " "Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file "
@ -399,29 +407,17 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
return result; return result;
} }
DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
{
bool result = false;
if (!Dqn_Str8_HasData(path))
return result;
struct stat stat_result;
if (lstat(path.data, &stat_result) != -1)
result = S_ISDIR(stat_result.st_mode);
return result;
}
DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path) DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
bool result = true; bool result = true;
// TODO(doyle): Implement this without using the path indexes, it's not // TODO(doyle): Implement this without using the path indexes, it's not
// necessary. See Windows implementation. // necessary. See Windows implementation.
Dqn_usize path_indexes_size = 0; Dqn_usize path_indexes_size = 0;
uint16_t path_indexes[64] = {}; uint16_t path_indexes[64] = {};
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, path); Dqn_Str8 copy = Dqn_Str8_Copy(tmem.arena, path);
for (Dqn_usize index = copy.size - 1; index < copy.size; index--) { for (Dqn_usize index = copy.size - 1; index < copy.size; index--) {
bool first_char = index == (copy.size - 1); bool first_char = index == (copy.size - 1);
char ch = copy.data[index]; char ch = copy.data[index];
@ -467,6 +463,51 @@ DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
return result; return result;
} }
DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
{
bool result = false;
if (!Dqn_Str8_HasData(path))
return result;
struct stat stat_result;
if (lstat(path.data, &stat_result) != -1)
result = S_ISDIR(stat_result.st_mode);
return result;
}
DQN_API bool Dqn_OS_DirIterate(Dqn_Str8 path, Dqn_OS_DirIterator *it)
{
if (!it->handle) {
it->handle = opendir(path.data);
if (!it->handle)
return false;
}
struct dirent *entry;
for (;;) {
entry = readdir(DQN_CAST(DIR*)it->handle);
if (entry == NULL)
break;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
Dqn_usize name_size = Dqn_CStr8_Size(entry->d_name);
Dqn_usize clamped_size = DQN_MIN(sizeof(it->buffer) - 1, name_size);
DQN_ASSERTF(name_size == clamped_size, "name: %s, name_size: %zu, clamped_size: %zu", entry->d_name, name_size, clamped_size);
DQN_MEMCPY(it->buffer, entry->d_name, clamped_size);
it->buffer[clamped_size] = 0;
it->file_name = Dqn_Str8_Init(it->buffer, clamped_size);
return true;
}
closedir(DQN_CAST(DIR*)it->handle);
it->handle = NULL;
it->file_name = {};
it->buffer[0] = 0;
return false;
}
// NOTE: R/W Stream API //////////////////////////////////////////////////////////////////////////// // NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path,
Dqn_OSFileOpen open_mode, Dqn_OSFileOpen open_mode,
@ -548,9 +589,9 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
return false; return false;
if (fread(buffer, size, 1, DQN_CAST(FILE *) file->handle) != 1) { if (fread(buffer, size, 1, DQN_CAST(FILE *) file->handle) != 1) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer_size_str8 = Dqn_Str8 buffer_size_str8 =
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8)); error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8));
return false; return false;
@ -568,9 +609,9 @@ Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize size, Dqn_Er
fwrite(buffer, DQN_CAST(Dqn_usize) size, 1 /*count*/, DQN_CAST(FILE *) file->handle) == fwrite(buffer, DQN_CAST(Dqn_usize) size, 1 /*count*/, DQN_CAST(FILE *) file->handle) ==
1 /*count*/; 1 /*count*/;
if (!result) { if (!result) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 buffer_size_str8 = Dqn_Str8 buffer_size_str8 =
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, 1, "Failed to write buffer (%s) to file handle", DQN_STR_FMT(buffer_size_str8)); error, 1, "Failed to write buffer (%s) to file handle", DQN_STR_FMT(buffer_size_str8));
} }
@ -663,17 +704,16 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle,
// NOTE: Read the data from the read end of the pipe // NOTE: Read the data from the read end of the pipe
if (result.os_error_code == 0) { if (result.os_error_code == 0) {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
if (arena && handle.stdout_read) { if (arena && handle.stdout_read) {
char buffer[4096]; char buffer[4096];
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
builder.arena = scratch.arena;
for (;;) { for (;;) {
ssize_t bytes_read = ssize_t bytes_read =
read(stdout_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer)); read(stdout_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
if (bytes_read <= 0) if (bytes_read <= 0)
break; break;
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer); Dqn_Str8Builder_AddF(&builder, "%.*s", bytes_read, buffer);
} }
result.stdout_text = Dqn_Str8Builder_Build(&builder, arena); result.stdout_text = Dqn_Str8Builder_Build(&builder, arena);
@ -681,14 +721,13 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle,
if (arena && handle.stderr_read) { if (arena && handle.stderr_read) {
char buffer[4096]; char buffer[4096];
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
builder.arena = scratch.arena;
for (;;) { for (;;) {
ssize_t bytes_read = ssize_t bytes_read =
read(stderr_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer)); read(stderr_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
if (bytes_read <= 0) if (bytes_read <= 0)
break; break;
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer); Dqn_Str8Builder_AddF(&builder, "%.*s", bytes_read, buffer);
} }
result.stderr_text = Dqn_Str8Builder_Build(&builder, arena); result.stderr_text = Dqn_Str8Builder_Build(&builder, arena);
@ -713,8 +752,8 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
if (cmd_line.size == 0) if (cmd_line.size == 0)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ")); Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(tmem.arena, cmd_line, DQN_STR8(" "));
int stdout_pipe[Dqn_OSPipeType__Count] = {}; int stdout_pipe[Dqn_OSPipeType__Count] = {};
int stderr_pipe[Dqn_OSPipeType__Count] = {}; int stderr_pipe[Dqn_OSPipeType__Count] = {};
@ -807,7 +846,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
// NOTE: Convert the command into something suitable for execvp // NOTE: Convert the command into something suitable for execvp
char **argv = char **argv =
Dqn_Arena_NewArray(scratch.arena, char *, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes); Dqn_Arena_NewArray(tmem.arena, char *, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
if (!argv) { if (!argv) {
result.exit_code = -1; result.exit_code = -1;
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
@ -820,7 +859,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; arg_index++) { for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; arg_index++) {
Dqn_Str8 arg = cmd_line.data[arg_index]; Dqn_Str8 arg = cmd_line.data[arg_index];
argv[arg_index] = Dqn_Str8_Copy(scratch.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated argv[arg_index] = Dqn_Str8_Copy(tmem.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
} }
// NOTE: Change the working directory if there is one // NOTE: Change the working directory if there is one
@ -1008,9 +1047,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex)
// NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// // NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
static void *Dqn_OS_ThreadFunc_(void *user_context) static void *Dqn_OS_ThreadFunc_(void *user_context)
{ {
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *) user_context; Dqn_OS_ThreadExecute_(user_context);
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
thread->func(thread);
return nullptr; return nullptr;
} }
@ -1167,12 +1204,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
response->arena = arena; response->arena = arena;
response->builder.arena = response->builder.arena =
response->scratch_arena ? response->scratch_arena : &response->tmp_arena; response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
Dqn_Arena *scratch_arena = response->scratch_arena; Dqn_Arena *tmem = response->tmem_arena;
Dqn_Scratch scratch_ = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem_ = Dqn_TLS_TMem(arena);
if (!scratch_arena) if (!tmem)
scratch_arena = scratch_.arena; tmem = tmem_.arena;
#if defined(DQN_PLATFORM_EMSCRIPTEN) #if defined(DQN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_attr_t fetch_attribs = {}; emscripten_fetch_attr_t fetch_attribs = {};

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "dqn.h" #include "dqn.h"
/*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
@ -15,8 +16,9 @@
// dqn_os_win32.cpp // dqn_os_win32.cpp
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
*/
// NOTE: [$VMEM] Dqn_OSMem ////////////////////////////////////////////////////////////////////////// // NOTE: [$VMEM] Dqn_OSMem /////////////////////////////////////////////////////////////////////////
static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect) static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect)
{ {
DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0); DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0);
@ -217,8 +219,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
Dqn_Str8 result = {}; Dqn_Str8 result = {};
if (!arena) if (!arena)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(scratch.arena); Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(t_mem.arena);
result = Dqn_Win_Str16ToStr8(arena, exe_dir16); result = Dqn_Win_Str16ToStr8(arena, exe_dir16);
return result; return result;
} }
@ -259,8 +261,8 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
if (!Dqn_Str8_HasData(path)) if (!Dqn_Str8_HasData(path))
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
@ -292,8 +294,8 @@ DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path)
if (!Dqn_Str8_HasData(path)) if (!Dqn_Str8_HasData(path))
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
if (path16.size) { if (path16.size) {
result = DeleteFileW(path16.data); result = DeleteFileW(path16.data);
if (!result) if (!result)
@ -308,8 +310,8 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
if (!Dqn_Str8_HasData(path)) if (!Dqn_Str8_HasData(path))
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
if (path16.size) { if (path16.size) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) {
@ -323,15 +325,15 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error) DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
{ {
bool result = false; bool result = false;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src);
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest);
int fail_if_exists = overwrite == false; int fail_if_exists = overwrite == false;
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
if (!result) { if (!result) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
win_error.code, win_error.code,
"Failed to copy file '%.*s' to '%.*s': (%u) %.*s", "Failed to copy file '%.*s' to '%.*s': (%u) %.*s",
@ -346,9 +348,9 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error) DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
{ {
bool result = false; bool result = false;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src);
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest);
unsigned long flags = MOVEFILE_COPY_ALLOWED; unsigned long flags = MOVEFILE_COPY_ALLOWED;
if (overwrite) { if (overwrite) {
@ -357,7 +359,7 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
result = MoveFileExW(src16.data, dest16.data, flags) != 0; result = MoveFileExW(src16.data, dest16.data, flags) != 0;
if (!result) { if (!result) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
win_error.code, win_error.code,
"Failed to move file '%.*s' to '%.*s': (%u) %.*s", "Failed to move file '%.*s' to '%.*s': (%u) %.*s",
@ -371,9 +373,9 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path) DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
{ {
bool result = true; bool result = true;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
// NOTE: Go back from the end of the string to all the directories in the // NOTE: Go back from the end of the string to all the directories in the
// string, and try to create them. Since Win32 API cannot create // string, and try to create them. Since Win32 API cannot create
@ -427,8 +429,8 @@ DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
if (!Dqn_Str8_HasData(path)) if (!Dqn_Str8_HasData(path))
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
if (path16.size) { if (path16.size) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) {
@ -474,8 +476,8 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
access_mode |= GENERIC_EXECUTE; access_mode |= GENERIC_EXECUTE;
} }
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
/*DWORD dwDesiredAccess*/ access_mode, /*DWORD dwDesiredAccess*/ access_mode,
/*DWORD dwShareMode*/ 0, /*DWORD dwShareMode*/ 0,
@ -485,7 +487,7 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
/*HANDLE hTemplateFile*/ nullptr); /*HANDLE hTemplateFile*/ nullptr);
if (handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.error = true; result.error = true;
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to open file at '%.*s': '%.*s'", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg)); Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to open file at '%.*s': '%.*s'", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg));
return result; return result;
@ -500,9 +502,9 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
if (!file || !file->handle || file->error || !buffer || size <= 0) if (!file || !file->handle || file->error || !buffer || size <= 0)
return false; return false;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
if (!DQN_CHECK(size <= (unsigned long)-1)) { if (!DQN_CHECK(size <= (unsigned long)-1)) {
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(t_mem.arena, size, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, error,
1 /*error_code*/, 1 /*error_code*/,
@ -518,13 +520,13 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
/*LPDWORD lpNumberOfByesRead*/ &bytes_read, /*LPDWORD lpNumberOfByesRead*/ &bytes_read,
/*LPOVERLAPPED lpOverlapped*/ nullptr); /*LPOVERLAPPED lpOverlapped*/ nullptr);
if (read_result == 0) { if (read_result == 0) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DQN_STR_FMT(win_error.msg)); Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DQN_STR_FMT(win_error.msg));
return false; return false;
} }
if (bytes_read != size) { if (bytes_read != size) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, error,
win_error.code, win_error.code,
@ -554,9 +556,9 @@ DQN_API bool Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize
} }
if (!result) { if (!result) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(t_mem.arena, size, Dqn_U64ByteSizeType_Auto);
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(win_error.msg)); Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(win_error.msg));
} }
return result; return result;
@ -594,69 +596,106 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle, Dqn_Arena
return result; return result;
} }
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
DWORD exec_result = WaitForSingleObject(handle.process, INFINITE); Dqn_Str8Builder stdout_builder = {};
CloseHandle(handle.stdout_write); Dqn_Str8Builder stderr_builder = {};
CloseHandle(handle.stderr_write); if (arena) {
stdout_builder.arena = t_mem.arena;
stderr_builder.arena = t_mem.arena;
}
if (exec_result == WAIT_FAILED) { DWORD const SLOW_WAIT_TIME_MS = 100;
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); DWORD const FAST_WAIT_TIME_MS = 20;
result.os_error_code = win_error.code; DWORD wait_ms = FAST_WAIT_TIME_MS;
Dqn_ErrorSink_MakeF(error, result.os_error_code, "Executed command failed to terminate: %.*s", DQN_STR_FMT(win_error.msg)); DWORD exec_result = WAIT_TIMEOUT;
CloseHandle(handle.process); while (exec_result == WAIT_TIMEOUT) {
} else { exec_result = WaitForSingleObject(handle.process, wait_ms);
// NOTE: Get exit code ///////////////////////////////////////////////////////////////////// if (exec_result == WAIT_FAILED) {
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF(error, result.os_error_code, "Executed command failed to terminate: %.*s", DQN_STR_FMT(win_error.msg));
} else if (DQN_CHECK(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
// NOTE: If the pipes are full, the process will block. We
// periodically flush the pipes to make sure this doesn't happen
// NOTE: Read stdout from process //////////////////////////////////////////////////////
// TODO: Allow the user to pump the execution of the command w/ a user passed in buffer.
char output_buffer[DQN_KILOBYTES(8)];
DWORD stdout_bytes_available = 0;
if (PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr)) {
if (stdout_bytes_available) {
for (;;) {
DWORD bytes_read = 0;
BOOL success = ReadFile(handle.stdout_read, output_buffer, sizeof(output_buffer), &bytes_read, NULL);
if (!success || bytes_read == 0)
break;
if (handle.stdout_write && arena)
Dqn_Str8Builder_AddF(&stdout_builder, "%.*s", bytes_read, output_buffer);
if (bytes_read < sizeof(output_buffer))
break;
}
}
}
// NOTE: Read stderr from process //////////////////////////////////////////////////////
DWORD stderr_bytes_available = 0;
if (PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr)) {
if (stderr_bytes_available) {
for (;;) {
DWORD bytes_read = 0;
BOOL success = ReadFile(handle.stderr_read, output_buffer, sizeof(output_buffer), &bytes_read, NULL);
if (!success || bytes_read == 0)
break;
if (handle.stderr_read && arena)
Dqn_Str8Builder_AddF(&stderr_builder, "%.*s", bytes_read, output_buffer);
if (bytes_read < sizeof(output_buffer))
break;
}
}
}
// NOTE: If we produced some data, we'll sleep for a short amount of
// time because if we have the presence of data it's typical we deal
// with N amount of. Otherwise if we timed-out and we didn't produce
// any data, its more likely this is a long running operation so we
// sleep longer to avoid slamming the CPU.
if (stdout_bytes_available || stderr_bytes_available) {
wait_ms = FAST_WAIT_TIME_MS;
} else {
wait_ms = SLOW_WAIT_TIME_MS;
}
}
}
// NOTE: Get stdout/stderr. If no arena is passed this is a no-op //////////////////////////////
result.stdout_text = Dqn_Str8Builder_Build(&stdout_builder, arena);
result.stderr_text = Dqn_Str8Builder_Build(&stderr_builder, arena);
// NOTE: Get exit code /////////////////////////////////////////////////////////////////////////
if (exec_result != WAIT_FAILED) {
DWORD exit_status; DWORD exit_status;
if (GetExitCodeProcess(handle.process, &exit_status)) { if (GetExitCodeProcess(handle.process, &exit_status)) {
result.exit_code = exit_status; result.exit_code = exit_status;
} else { } else {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
result.os_error_code, result.os_error_code,
"Failed to retrieve command exit code: %.*s", "Failed to retrieve command exit code: %.*s",
DQN_STR_FMT(win_error.msg)); DQN_STR_FMT(win_error.msg));
} }
CloseHandle(handle.process);
// NOTE: Read stdout from process //////////////////////////////////////////////////////////
if (arena && handle.stdout_write) {
char stdout_buffer[4096];
Dqn_Str8Builder builder = {};
builder.arena = scratch.arena;
for (;;) {
DWORD bytes_read = 0;
BOOL success = ReadFile(handle.stdout_read, stdout_buffer, sizeof(stdout_buffer), &bytes_read, NULL);
if (!success || bytes_read == 0)
break;
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, stdout_buffer);
}
result.stdout_text = Dqn_Str8Builder_Build(&builder, arena);
}
// NOTE: Read stderr from process //////////////////////////////////////////////////////////
if (arena && handle.stderr_read) {
char stderr_buffer[4096];
Dqn_Str8Builder builder = {};
builder.arena = scratch.arena;
for (;;) {
DWORD bytes_read = 0;
BOOL success = ReadFile(handle.stderr_read, stderr_buffer, sizeof(stderr_buffer), &bytes_read, NULL);
if (!success || bytes_read == 0)
break;
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, stderr_buffer);
}
result.stderr_text = Dqn_Str8Builder_Build(&builder, arena);
}
} }
// NOTE: Cleanup ///////////////////////////////////////////////////////////////////////////////
CloseHandle(handle.stdout_write);
CloseHandle(handle.stderr_write);
CloseHandle(handle.stdout_read); CloseHandle(handle.stdout_read);
CloseHandle(handle.stderr_read); CloseHandle(handle.stderr_read);
CloseHandle(handle.process);
return result; return result;
} }
@ -667,10 +706,10 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
if (cmd_line.size == 0) if (cmd_line.size == 0)
return result; return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ")); Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(t_mem.arena, cmd_line, DQN_STR8(" "));
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd_rendered); Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(t_mem.arena, cmd_rendered);
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir); Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(t_mem.arena, working_dir);
// NOTE: Stdout/err security attributes //////////////////////////////////////////////////////// // NOTE: Stdout/err security attributes ////////////////////////////////////////////////////////
SECURITY_ATTRIBUTES save_std_security_attribs = {}; SECURITY_ATTRIBUTES save_std_security_attribs = {};
@ -689,7 +728,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout)) { if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout)) {
if (!CreatePipe(&stdout_read, &stdout_write, &save_std_security_attribs, /*nSize*/ 0)) { if (!CreatePipe(&stdout_read, &stdout_write, &save_std_security_attribs, /*nSize*/ 0)) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, error,
@ -701,7 +740,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
} }
if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) { if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
result.os_error_code, result.os_error_code,
@ -729,7 +768,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
stderr_write = stdout_write; stderr_write = stdout_write;
} else { } else {
if (!CreatePipe(&stderr_read, &stderr_write, &save_std_security_attribs, /*nSize*/ 0)) { if (!CreatePipe(&stderr_read, &stderr_write, &save_std_security_attribs, /*nSize*/ 0)) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF( Dqn_ErrorSink_MakeF(
error, error,
@ -741,7 +780,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
} }
if (!SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) { if (!SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
result.os_error_code, result.os_error_code,
@ -764,7 +803,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
startup_info.dwFlags |= STARTF_USESTDHANDLES; startup_info.dwFlags |= STARTF_USESTDHANDLES;
BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info); BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info);
if (!create_result) { if (!create_result) {
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
result.os_error_code = win_error.code; result.os_error_code = win_error.code;
Dqn_ErrorSink_MakeF(error, Dqn_ErrorSink_MakeF(error,
result.os_error_code, result.os_error_code,
@ -882,9 +921,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex)
// NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// // NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
static DWORD __stdcall Dqn_OS_ThreadFunc_(void *user_context) static DWORD __stdcall Dqn_OS_ThreadFunc_(void *user_context)
{ {
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context; Dqn_OS_ThreadExecute_(user_context);
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
thread->func(thread);
return 0; return 0;
} }
@ -1001,7 +1038,7 @@ void Dqn_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD
DWORD bytes_read = dwStatusInformationLength; DWORD bytes_read = dwStatusInformationLength;
if (bytes_read) { if (bytes_read) {
Dqn_Str8 prev_buffer = Dqn_Str8_Init(DQN_CAST(char *) lpvStatusInformation, bytes_read); Dqn_Str8 prev_buffer = Dqn_Str8_Init(DQN_CAST(char *) lpvStatusInformation, bytes_read);
Dqn_Str8Builder_AppendRef(&response->builder, prev_buffer); Dqn_Str8Builder_AddRef(&response->builder, prev_buffer);
void *buffer = Dqn_Arena_Alloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, Dqn_ZeroMem_No); void *buffer = Dqn_Arena_Alloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, Dqn_ZeroMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
@ -1050,12 +1087,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
return; return;
response->arena = arena; response->arena = arena;
response->builder.arena = response->scratch_arena ? response->scratch_arena : &response->tmp_arena; response->builder.arena = response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
Dqn_Arena *scratch_arena = response->scratch_arena; Dqn_Arena *t_mem_arena = response->tmem_arena;
Dqn_Scratch scratch_ = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem_ = Dqn_TLS_TMem(arena);
if (!scratch_arena) { if (!t_mem_arena) {
scratch_arena = scratch_.arena; t_mem_arena = t_mem_.arena;
} }
Dqn_WinError error = {}; Dqn_WinError error = {};
@ -1088,15 +1125,15 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
return; return;
} }
Dqn_Str16 host16 = Dqn_Win_Str8ToStr16(scratch_arena, host); Dqn_Str16 host16 = Dqn_Win_Str8ToStr16(t_mem_arena, host);
response->win32_request_connection = WinHttpConnect(response->win32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/); response->win32_request_connection = WinHttpConnect(response->win32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
if (!response->win32_request_connection) { if (!response->win32_request_connection) {
error = Dqn_Win_LastError(&response->tmp_arena); error = Dqn_Win_LastError(&response->tmp_arena);
return; return;
} }
Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(scratch_arena, method); Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(t_mem_arena, method);
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch_arena, path); Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem_arena, path);
response->win32_request_handle = WinHttpOpenRequest(response->win32_request_connection, response->win32_request_handle = WinHttpOpenRequest(response->win32_request_connection,
method16.data, method16.data,
path16.data, path16.data,
@ -1109,7 +1146,7 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
return; return;
} }
Dqn_Str16 headers16 = Dqn_Win_Str8ToStr16(scratch_arena, headers); Dqn_Str16 headers16 = Dqn_Win_Str8ToStr16(t_mem_arena, headers);
response->on_complete_semaphore = Dqn_OS_SemaphoreInit(0); response->on_complete_semaphore = Dqn_OS_SemaphoreInit(0);
if (!WinHttpSendRequest(response->win32_request_handle, if (!WinHttpSendRequest(response->win32_request_handle,
headers16.data, headers16.data,
@ -1295,13 +1332,13 @@ DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8(Dqn_Arena *arena, Dqn_Str16 src)
// NOTE: Windows Executable Directory ////////////////////////////////////////// // NOTE: Windows Executable Directory //////////////////////////////////////////
DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena) DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
Dqn_Str16 result = {}; Dqn_Str16 result = {};
Dqn_usize module_size = 0; Dqn_usize module_size = 0;
wchar_t *module_path = nullptr; wchar_t *module_path = nullptr;
do { do {
module_size += 256; module_size += 256;
module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No); module_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, module_size, Dqn_ZeroMem_No);
if (!module_path) if (!module_path)
return result; return result;
module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size); module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size);
@ -1321,13 +1358,13 @@ DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena)
DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena) DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena)
{ {
// TODO(doyle): Implement a Dqn_Str16_BinarySearchReverse // TODO(doyle): Implement a Dqn_Str16_BinarySearchReverse
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
Dqn_Str16 result = {}; Dqn_Str16 result = {};
Dqn_usize module_size = 0; Dqn_usize module_size = 0;
wchar_t *module_path = nullptr; wchar_t *module_path = nullptr;
do { do {
module_size += 256; module_size += 256;
module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No); module_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, module_size, Dqn_ZeroMem_No);
if (!module_path) if (!module_path)
return result; return result;
module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size); module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size);
@ -1346,9 +1383,9 @@ DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena)
DQN_API Dqn_Str8 Dqn_Win_WorkingDir(Dqn_Arena *arena, Dqn_Str8 suffix) DQN_API Dqn_Str8 Dqn_Win_WorkingDir(Dqn_Arena *arena, Dqn_Str8 suffix)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(scratch.arena, suffix); Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(t_mem.arena, suffix);
Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(scratch.arena, suffix16); Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(t_mem.arena, suffix16);
Dqn_Str8 result = Dqn_Win_Str16ToStr8(arena, dir16); Dqn_Str8 result = Dqn_Win_Str16ToStr8(arena, dir16);
return result; return result;
} }
@ -1359,15 +1396,15 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix)
Dqn_Str16 result = {}; Dqn_Str16 result = {};
// NOTE: required_size is the size required *including* the null-terminator // NOTE: required_size is the size required *including* the null-terminator
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
unsigned long required_size = GetCurrentDirectoryW(0, nullptr); unsigned long required_size = GetCurrentDirectoryW(0, nullptr);
unsigned long desired_size = required_size + DQN_CAST(unsigned long) suffix.size; unsigned long desired_size = required_size + DQN_CAST(unsigned long) suffix.size;
wchar_t *scratch_w_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, desired_size, Dqn_ZeroMem_No); wchar_t *t_mem_w_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, desired_size, Dqn_ZeroMem_No);
if (!scratch_w_path) if (!t_mem_w_path)
return result; return result;
unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, scratch_w_path); unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, t_mem_w_path);
if ((bytes_written_wo_null_terminator + 1) != required_size) { if ((bytes_written_wo_null_terminator + 1) != required_size) {
// TODO(dqn): Error // TODO(dqn): Error
return result; return result;
@ -1378,7 +1415,7 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix)
return result; return result;
if (suffix.size) { if (suffix.size) {
DQN_MEMCPY(w_path, scratch_w_path, sizeof(*scratch_w_path) * bytes_written_wo_null_terminator); DQN_MEMCPY(w_path, t_mem_w_path, sizeof(*t_mem_w_path) * bytes_written_wo_null_terminator);
DQN_MEMCPY(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.size); DQN_MEMCPY(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.size);
w_path[desired_size] = 0; w_path[desired_size] = 0;
} }
@ -1427,7 +1464,7 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_Str8 path, Dqn_Win_FolderIterator *it)
if (!Dqn_Str8_HasData(path) || !it || path.size <= 0) if (!Dqn_Str8_HasData(path) || !it || path.size <= 0)
return false; return false;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
Dqn_Win_FolderIteratorW wide_it = {}; Dqn_Win_FolderIteratorW wide_it = {};
Dqn_Str16 path16 = {}; Dqn_Str16 path16 = {};
if (it->handle) { if (it->handle) {
@ -1444,12 +1481,12 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_Str8 path, Dqn_Win_FolderIterator *it)
// add those characters in this branch, so overwrite the null // add those characters in this branch, so overwrite the null
// character, add the glob and re-null terminate the buffer. // character, add the glob and re-null terminate the buffer.
if (needs_asterisks) if (needs_asterisks)
adjusted_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s*", DQN_STR_FMT(path)); adjusted_path = Dqn_OS_PathF(t_mem.arena, "%.*s*", DQN_STR_FMT(path));
else else
adjusted_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/*", DQN_STR_FMT(path)); adjusted_path = Dqn_OS_PathF(t_mem.arena, "%.*s/*", DQN_STR_FMT(path));
} }
path16 = Dqn_Win_Str8ToStr16(scratch.arena, adjusted_path); path16 = Dqn_Win_Str8ToStr16(t_mem.arena, adjusted_path);
if (path16.size <= 0) // Conversion error if (path16.size <= 0) // Conversion error
return false; return false;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "dqn.h" #include "dqn.h"
/*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
@ -15,6 +16,7 @@
// dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer // dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
*/
struct Dqn_WinError struct Dqn_WinError
{ {

View File

@ -95,7 +95,7 @@ DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
case Dqn_Str8IsAll_Hex: { case Dqn_Str8IsAll_Hex: {
Dqn_Str8 trimmed = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive); Dqn_Str8 trimmed = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive);
for (Dqn_usize index = 0; result && index < string.size; index++) { for (Dqn_usize index = 0; result && index < trimmed.size; index++) {
char ch = trimmed.data[index]; char ch = trimmed.data[index];
result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
} }
@ -221,7 +221,7 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin
return result; return result;
} }
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size, Dqn_Str8EqCase eq_case)
{ {
Dqn_Str8FindResult result = {}; Dqn_Str8FindResult result = {};
if (!Dqn_Str8_HasData(string) || !find || find_size == 0) if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
@ -231,7 +231,7 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 cons
for (Dqn_usize find_index = 0; find_index < find_size; find_index++) { for (Dqn_usize find_index = 0; find_index < find_size; find_index++) {
Dqn_Str8 find_item = find[find_index]; Dqn_Str8 find_item = find[find_index];
Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size); Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size);
if (Dqn_Str8_Eq(string_slice, find_item)) { if (Dqn_Str8_Eq(string_slice, find_item, eq_case)) {
result.found = true; result.found = true;
result.index = index; result.index = index;
result.start_to_before_match = Dqn_Str8_Init(string.data, index); result.start_to_before_match = Dqn_Str8_Init(string.data, index);
@ -244,9 +244,9 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 cons
return result; return result;
} }
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find) DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8EqCase eq_case)
{ {
Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1); Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1, eq_case);
return result; return result;
} }
@ -403,6 +403,12 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix(Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8E
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_Str8_TrimHexPrefix(Dqn_Str8 string)
{
Dqn_Str8 result = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive);
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix(Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case) DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix(Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case)
{ {
Dqn_Str8 result = string; Dqn_Str8 result = string;
@ -591,11 +597,10 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
return result; return result;
} }
Dqn_Scratch scratch = Dqn_Scratch_Get(arena); Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
Dqn_Str8Builder string_builder = {}; Dqn_Str8Builder string_builder = Dqn_Str8Builder_Init(tmem.arena);
string_builder.arena = scratch.arena; Dqn_usize max = string.size - find.size;
Dqn_usize max = string.size - find.size; Dqn_usize head = start_index;
Dqn_usize head = start_index;
for (Dqn_usize tail = head; tail <= max; tail++) { for (Dqn_usize tail = head; tail <= max; tail++) {
Dqn_Str8 check = Dqn_Str8_Slice(string, tail, find.size); Dqn_Str8 check = Dqn_Str8_Slice(string, tail, find.size);
@ -608,12 +613,12 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
// a replacement action, otherwise we have a special case for no // a replacement action, otherwise we have a special case for no
// replacements, where the entire string gets copied. // replacements, where the entire string gets copied.
Dqn_Str8 slice = Dqn_Str8_Init(string.data, head); Dqn_Str8 slice = Dqn_Str8_Init(string.data, head);
Dqn_Str8Builder_AppendRef(&string_builder, slice); Dqn_Str8Builder_AddRef(&string_builder, slice);
} }
Dqn_Str8 range = Dqn_Str8_Slice(string, head, (tail - head)); Dqn_Str8 range = Dqn_Str8_Slice(string, head, (tail - head));
Dqn_Str8Builder_AppendRef(&string_builder, range); Dqn_Str8Builder_AddRef(&string_builder, range);
Dqn_Str8Builder_AppendRef(&string_builder, replace); Dqn_Str8Builder_AddRef(&string_builder, replace);
head = tail + find.size; head = tail + find.size;
tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string
} }
@ -623,7 +628,7 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
result = Dqn_Str8_Copy(arena, string); result = Dqn_Str8_Copy(arena, string);
} else { } else {
Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head); Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head);
Dqn_Str8Builder_AppendRef(&string_builder, remainder); Dqn_Str8Builder_AddRef(&string_builder, remainder);
result = Dqn_Str8Builder_Build(&string_builder, arena); result = Dqn_Str8Builder_Build(&string_builder, arena);
} }
@ -693,6 +698,8 @@ DQN_API Dqn_Str8 Dqn_Str8_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem ze
result.data = Dqn_Arena_NewArray(arena, char, size + 1, zero_mem); result.data = Dqn_Arena_NewArray(arena, char, size + 1, zero_mem);
if (result.data) if (result.data)
result.size = size; result.size = size;
if (zero_mem == Dqn_ZeroMem_No)
result.data[result.size] = 0;
return result; return result;
} }
@ -717,18 +724,46 @@ DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Arena *arena, Dqn_Str8 string)
} }
// NOTE: [$STRB] Dqn_Str8Builder //////////////////////////////////////////////////////////////// // NOTE: [$STRB] Dqn_Str8Builder ////////////////////////////////////////////////////////////////
DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array) DQN_API Dqn_Str8Builder Dqn_Str8Builder_Init(Dqn_Arena *arena)
{
Dqn_Str8Builder result = {};
result.arena = arena;
return result;
}
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayRef(Dqn_Arena *arena,
Dqn_Str8 const *strings,
Dqn_usize size)
{
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
Dqn_Str8Builder_AddArrayRef(&result, strings, size);
return result;
}
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayCopy(Dqn_Arena *arena,
Dqn_Str8 const *strings,
Dqn_usize size)
{
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
Dqn_Str8Builder_AddArrayCopy(&result, strings, size);
return result;
}
DQN_API bool Dqn_Str8Builder_AddArrayRef(Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size)
{ {
if (!builder) if (!builder)
return false; return false;
for (Dqn_Str8 string : array) { if (!strings || size <= 0)
if (!builder || !string.data || string.size <= 0) return true;
return false;
Dqn_Str8Link *link = Dqn_Arena_New(builder->arena, Dqn_Str8Link, Dqn_ZeroMem_No); Dqn_Str8Link *links = Dqn_Arena_NewArray(builder->arena, Dqn_Str8Link, size, Dqn_ZeroMem_No);
if (!link) if (!links)
return false; return false;
DQN_FOR_UINDEX(index, size) {
Dqn_Str8 string = strings[index];
Dqn_Str8Link *link = links + index;
link->string = string; link->string = string;
link->next = NULL; link->next = NULL;
@ -746,69 +781,203 @@ DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<
return true; return true;
} }
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array) DQN_API bool Dqn_Str8Builder_AddArrayCopy(Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size)
{ {
for (Dqn_Str8 string : array) { if (!builder)
Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string); return false;
if (!Dqn_Str8Builder_AppendRef(builder, copy))
return false;
}
return true;
}
DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args) if (!strings || size <= 0)
{
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
if (string.size == 0)
return true; return true;
Dqn_ArenaTempMem tmp_mem = Dqn_Arena_TempMemBegin(builder->arena);
bool result = true;
Dqn_Str8 *strings_copy = Dqn_Arena_NewArray(builder->arena, Dqn_Str8, size, Dqn_ZeroMem_No);
DQN_FOR_UINDEX (index, size) {
strings_copy[index] = Dqn_Str8_Copy(builder->arena, strings[index]);
if (strings_copy[index].size != strings[index].size) {
result = false;
break;
}
}
if (result)
result = Dqn_Str8Builder_AddArrayRef(builder, strings_copy, size);
if (!result)
Dqn_Arena_TempMemEnd(tmp_mem);
return result;
}
DQN_API bool Dqn_Str8Builder_AddSliceRef(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
{
bool result = Dqn_Str8Builder_AddArrayRef(builder, array.data, array.size);
return result;
}
DQN_API bool Dqn_Str8Builder_AddSliceCopy(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
{
bool result = Dqn_Str8Builder_AddArrayCopy(builder, array.data, array.size);
return result;
}
DQN_API bool Dqn_Str8Builder_AddRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
{
bool result = Dqn_Str8Builder_AddArrayRef(builder, &string, 1);
return result;
}
DQN_API bool Dqn_Str8Builder_AddCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
{
bool result = Dqn_Str8Builder_AddArrayCopy(builder, &string, 1);
return result;
}
DQN_API bool Dqn_Str8Builder_AddFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena); Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena);
bool result = Dqn_Str8Builder_AppendRef(builder, string); bool result = Dqn_Str8Builder_AddRef(builder, string);
if (!result) if (!result)
Dqn_Arena_TempMemEnd(temp_mem); Dqn_Arena_TempMemEnd(temp_mem);
return result; return result;
} }
DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...) DQN_API bool Dqn_Str8Builder_AddF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
bool result = Dqn_Str8Builder_AppendFV(builder, fmt, args); bool result = Dqn_Str8Builder_AddFV(builder, fmt, args);
va_end(args); va_end(args);
return result; return result;
} }
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string) DQN_API bool Dqn_Str8Builder_AddBytesRef(Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size)
{ {
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1); Dqn_Str8 input = Dqn_Str8_Init(ptr, size);
bool result = Dqn_Str8Builder_AppendRefArray(builder, array); bool result = Dqn_Str8Builder_AddRef(builder, input);
return result; return result;
} }
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string) DQN_API bool Dqn_Str8Builder_AddBytesCopy(Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size)
{ {
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1); Dqn_Str8 input = Dqn_Str8_Init(ptr, size);
bool result = Dqn_Str8Builder_AppendCopyArray(builder, array); bool result = Dqn_Str8Builder_AddCopy(builder, input);
return result;
}
static bool Dqn_Str8Builder_AddBuilder_(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src, bool copy)
{
if (!dest)
return false;
if (!src)
return true;
Dqn_Arena_TempMemBegin(dest->arena);
Dqn_Str8Link *links = Dqn_Arena_NewArray(dest->arena, Dqn_Str8Link, src->count, Dqn_ZeroMem_No);
if (!links)
return false;
Dqn_Str8Link *first = nullptr;
Dqn_Str8Link *last = nullptr;
Dqn_usize link_index = 0;
bool result = true;
for (Dqn_Str8Link const *it = src->head; it; it = it->next) {
Dqn_Str8Link *link = links + link_index++;
link->next = nullptr;
link->string = it->string;
if (copy) {
link->string = Dqn_Str8_Copy(dest->arena, it->string);
if (link->string.size != it->string.size) {
result = false;
break;
}
}
if (last) {
last->next = link;
} else {
first = link;
}
last = link;
}
if (result) {
if (dest->head)
dest->tail->next = first;
else
dest->head = first;
dest->tail = last;
dest->count += src->count;
dest->string_size += src->string_size;
}
return true;
}
DQN_API bool Dqn_Str8Builder_AddBuilderRef(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src)
{
bool result = Dqn_Str8Builder_AddBuilder_(dest, src, false);
return result;
}
DQN_API bool Dqn_Str8Builder_AddBuilderCopy(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src)
{
bool result = Dqn_Str8Builder_AddBuilder_(dest, src, true);
return result;
}
DQN_API bool Dqn_Str8Builder_Erase(Dqn_Str8Builder *builder, Dqn_Str8 string)
{
for (Dqn_Str8Link **it = &builder->head; *it; it = &((*it)->next)) {
if ((*it)->string == string) {
*it = (*it)->next;
builder->string_size -= string.size;
builder->count -= 1;
return true;
}
}
return false;
}
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Copy(Dqn_Arena *arena, Dqn_Str8Builder const *builder)
{
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
Dqn_Str8Builder_AddBuilderCopy(&result, builder);
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Arena *arena) DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
{
Dqn_Str8 result = Dqn_Str8Builder_BuildDelimited(builder, DQN_STR8(""), arena);
return result;
}
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildDelimited(Dqn_Str8Builder const *builder, Dqn_Str8 delimiter, Dqn_Arena *arena)
{ {
Dqn_Str8 result = DQN_ZERO_INIT; Dqn_Str8 result = DQN_ZERO_INIT;
if (!builder || builder->string_size <= 0 || builder->count <= 0) if (!builder || builder->string_size <= 0 || builder->count <= 0)
return result; return result;
result.data = Dqn_Arena_NewArray(arena, char, builder->string_size + 1, Dqn_ZeroMem_No); Dqn_usize size_for_delimiter = Dqn_Str8_HasData(delimiter) ? ((builder->count - 1) * delimiter.size) : 0;
result.data = Dqn_Arena_NewArray(arena,
char,
builder->string_size + size_for_delimiter + 1 /*null terminator*/,
Dqn_ZeroMem_No);
if (!result.data) if (!result.data)
return result; return result;
for (Dqn_Str8Link *link = builder->head; link; link = link->next) { for (Dqn_Str8Link *link = builder->head; link; link = link->next) {
DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size); DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size);
result.size += link->string.size; result.size += link->string.size;
if (link->next && Dqn_Str8_HasData(delimiter)) {
DQN_MEMCPY(result.data + result.size, delimiter.data, delimiter.size);
result.size += delimiter.size;
}
} }
result.data[result.size] = 0; result.data[result.size] = 0;
DQN_ASSERT(result.size == builder->string_size); DQN_ASSERT(result.size == builder->string_size + size_for_delimiter);
return result; return result;
} }
@ -850,13 +1019,13 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice(Dqn_Str8Builder const *bu
return result; return result;
} }
DQN_API void Dqn_Str8Builder_PrintF(Dqn_Str8Builder const *builder) DQN_API void Dqn_Str8Builder_Print(Dqn_Str8Builder const *builder)
{ {
for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next) for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next)
Dqn_Print(link->string); Dqn_Print(link->string);
} }
DQN_API void Dqn_Str8Builder_PrintLnF(Dqn_Str8Builder const *builder) DQN_API void Dqn_Str8Builder_PrintLn(Dqn_Str8Builder const *builder)
{ {
for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next) { for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next) {
if (link->next) { if (link->next) {

View File

@ -143,88 +143,123 @@ DQN_API bool operator!=
#endif #endif
// NOTE: [$STR8] Dqn_Str8 ////////////////////////////////////////////////////////////////////////// // NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
#define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)} #define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)}
#define DQN_STR_FMT(string) (int)((string).size), (string).data #define DQN_STR_FMT(string) (int)((string).size), (string).data
#define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)} #define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)}
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src); DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src);
#define Dqn_Str8_HasData(string) ((string).data && (string).size) #define Dqn_Str8_HasData(string) ((string).data && (string).size)
DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all); DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all);
DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args); #define Dqn_Str8_InitF_TLS(...) Dqn_Str8_InitF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size); #define Dqn_Str8_InitFV_TLS(...) Dqn_Str8_InitFV(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string); DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
#define Dqn_Str8_Alloc_TLS(...) Dqn_Str8_Alloc(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size);
#define Dqn_Str8_CopyCString_TLS(...) Dqn_Str8_CopyCString(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string);
#define Dqn_Str8_Copy_TLS(...) Dqn_Str8_Copy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size); DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size);
DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount); DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount);
DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string); DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find); DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (Dqn_Str8 string, Dqn_Str8 find); DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode); DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode); DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
#define Dqn_Str8_SplitAlloc_TLS(...) Dqn_Str8_SplitAlloc(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size, Dqn_Str8EqCase eq_case);
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find); DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8EqCase eq_case);
DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags); DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags);
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); #define Dqn_Str8_Segment_TLS(...) Dqn_Str8_Segment(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
#define Dqn_Str8_ReverseSegment_TLS(...) Dqn_Str8_ReverseSegment(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs); DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs);
DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix); DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix); DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch); DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch);
DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API Dqn_Str8 Dqn_Str8_TrimHexPrefix (Dqn_Str8 string);
DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string); DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string); DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string);
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string); DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string);
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string);
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path); DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path);
DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path); DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path);
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path); DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path);
DQN_API Dqn_Str8 Dqn_Str8_FileExtension (Dqn_Str8 path); DQN_API Dqn_Str8 Dqn_Str8_FileExtension (Dqn_Str8 path);
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator); DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator);
DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator); DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator);
DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena); DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena);
DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size); DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size);
#if defined(__cplusplus) #if defined(__cplusplus)
DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs); DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs); DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
#endif #endif
// NOTE: [$FSTR] Dqn_Str8Builder /////////////////////////////////////////////////////////////////// // NOTE: [$STRB] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
DQN_API bool Dqn_Str8Builder_AppendRefArray (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string); DQN_API Dqn_Str8Builder Dqn_Str8Builder_Init (Dqn_Arena *arena);
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string); #define Dqn_Str8Builder_Init_TLS() Dqn_Str8Builder_Init(Dqn_TLS_TopArena())
DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args); DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayRef (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size);
DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...); #define Dqn_Str8Builder_InitArrayRef_TLS(...) Dqn_Str8Builder_InitArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string); DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayCopy (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size);
DQN_API bool Dqn_Str8Builder_AppendCopy (Dqn_Str8Builder *builder, Dqn_Str8 string); #define Dqn_Str8Builder_InitArrayCopy_TLS(...) Dqn_Str8Builder_InitArrayCopy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena); template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayRef (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]);
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder); #define Dqn_Str8Builder_InitCArrayRef_TLS(...) Dqn_Str8Builder_InitCArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena); template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayCopy (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]);
DQN_API void Dqn_Str8Builder_PrintF (Dqn_Str8Builder const *builder); #define Dqn_Str8Builder_InitCArrayCopy_TLS(...) Dqn_Str8Builder_InitCArrayCopy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API bool Dqn_Str8Builder_AddArrayRef (Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size);
DQN_API bool Dqn_Str8Builder_AddArrayCopy (Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size);
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayRef (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]);
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayCopy (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]);
DQN_API bool Dqn_Str8Builder_AddSliceRef (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
DQN_API bool Dqn_Str8Builder_AddSliceCopy (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
DQN_API bool Dqn_Str8Builder_AddRef (Dqn_Str8Builder *builder, Dqn_Str8 string);
DQN_API bool Dqn_Str8Builder_AddCopy (Dqn_Str8Builder *builder, Dqn_Str8 string);
DQN_API bool Dqn_Str8Builder_AddFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_Str8Builder_AddF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API bool Dqn_Str8Builder_AddBytesRef (Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size);
DQN_API bool Dqn_Str8Builder_AddBytesCopy (Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size);
DQN_API bool Dqn_Str8Builder_AddBuilderRef (Dqn_Str8Builder *dest, Dqn_Str8Builder const *src);
DQN_API bool Dqn_Str8Builder_AddBuilderCopy (Dqn_Str8Builder *dest, Dqn_Str8Builder const *src);
DQN_API bool Dqn_Str8Builder_Erase (Dqn_Str8Builder *builder, Dqn_Str8 string);
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Copy (Dqn_Arena *arena, Dqn_Str8Builder const *builder);
#define Dqn_Str8Builder_Copy_TLS(...) Dqn_Str8Builder_Copy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
#define Dqn_Str8Builder_Build_TLS(...) Dqn_Str8Builder_Build(__VA_ARGS__, Dqn_TLS_TopArena())
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildDelimited (Dqn_Str8Builder const *builder, Dqn_Str8 delimiter, Dqn_Arena *arena);
#define Dqn_Str8Builder_BuildDelimited_TLS(...) Dqn_Str8Builder_BuildDelimited(__VA_ARGS__, Dqn_TLS_TopArena())
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder);
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
#define Dqn_Str8Builder_BuildSlice_TLS(...) Dqn_Str8Builder_BuildSlice(__VA_ARGS__, Dqn_TLS_TopArena())
DQN_API void Dqn_Str8Builder_Print (Dqn_Str8Builder const *builder);
DQN_API void Dqn_Str8Builder_PrintLn (Dqn_Str8Builder const *builder);
// NOTE: [$FSTR] Dqn_FStr8 ////////////////////////////////////////////////////////////////////// // NOTE: [$FSTR] Dqn_FStr8 //////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_FSTR8) #if !defined(DQN_NO_FSTR8)
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...); template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...);
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max (Dqn_FStr8<N> const *string); template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max (Dqn_FStr8<N> const *string);
template <Dqn_usize N> void Dqn_FStr8_Clear (Dqn_FStr8<N> *string); template <Dqn_usize N> void Dqn_FStr8_Clear (Dqn_FStr8<N> *string);
template <Dqn_usize N> bool Dqn_FStr8_AppendFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va); template <Dqn_usize N> bool Dqn_FStr8_AddFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va);
template <Dqn_usize N> bool Dqn_FStr8_AppendF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...); template <Dqn_usize N> bool Dqn_FStr8_AddF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...);
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size); template <Dqn_usize N> bool Dqn_FStr8_AddCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size);
template <Dqn_usize N> bool Dqn_FStr8_Append (Dqn_FStr8<N> *string, Dqn_Str8 value); template <Dqn_usize N> bool Dqn_FStr8_Add (Dqn_FStr8<N> *string, Dqn_Str8 value);
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8<N> const *string); template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8<N> const *string);
template <Dqn_usize N> bool Dqn_FStr8_Eq (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case); template <Dqn_usize N> bool Dqn_FStr8_Eq (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case);
template <Dqn_usize N> bool Dqn_FStr8_EqStr8 (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case); template <Dqn_usize N> bool Dqn_FStr8_EqStr8 (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case);
@ -257,6 +292,31 @@ DQN_API char Dqn_Char_ToLower
DQN_API int Dqn_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint); DQN_API int Dqn_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint);
DQN_API int Dqn_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint); DQN_API int Dqn_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint);
// NOTE: [$STRB] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayRef(Dqn_Arena *arena, Dqn_Str8 const (&array)[N])
{
Dqn_Str8Builder result = Dqn_Str8Builder_InitArrayRef(arena, array, N);
return result;
}
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayCopy(Dqn_Arena *arena, Dqn_Str8 const (&array)[N])
{
Dqn_Str8Builder result = Dqn_Str8Builder_InitArrayCopy(arena, array, N);
return result;
}
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayRef(Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N])
{
bool result = Dqn_Str8Builder_AddArrayRef(builder, array, N);
return result;
}
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayCopy(Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N])
{
bool result = Dqn_Str8Builder_AddArrayCopy(builder, array, N);
return result;
}
#if !defined(DQN_NO_FSTR8) #if !defined(DQN_NO_FSTR8)
// NOTE: [$FSTR] Dqn_FStr8 ///////////////////////////////////////////////////////////////////////// // NOTE: [$FSTR] Dqn_FStr8 /////////////////////////////////////////////////////////////////////////
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...) template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...)
@ -265,7 +325,7 @@ template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *f
if (fmt) { if (fmt) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_FStr8_AppendFV(&result, fmt, args); Dqn_FStr8_AddFV(&result, fmt, args);
va_end(args); va_end(args);
} }
return result; return result;
@ -282,7 +342,7 @@ template <Dqn_usize N> void Dqn_FStr8_Clear(Dqn_FStr8<N> *string)
*string = {}; *string = {};
} }
template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args) template <Dqn_usize N> bool Dqn_FStr8_AddFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args)
{ {
bool result = false; bool result = false;
if (!string || !fmt) if (!string || !fmt)
@ -299,19 +359,19 @@ template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATT
return result; return result;
} }
template <Dqn_usize N> bool Dqn_FStr8_AppendF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...) template <Dqn_usize N> bool Dqn_FStr8_AddF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
bool result = false; bool result = false;
if (!string || !fmt) if (!string || !fmt)
return result; return result;
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
result = Dqn_FStr8_AppendFV(string, fmt, args); result = Dqn_FStr8_AddFV(string, fmt, args);
va_end(args); va_end(args);
return result; return result;
} }
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size) template <Dqn_usize N> bool Dqn_FStr8_AddCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size)
{ {
DQN_ASSERT(string->size <= N); DQN_ASSERT(string->size <= N);
bool result = false; bool result = false;
@ -326,9 +386,9 @@ template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char con
return result; return result;
} }
template <Dqn_usize N> bool Dqn_FStr8_Append(Dqn_FStr8<N> *string, Dqn_Str8 src) template <Dqn_usize N> bool Dqn_FStr8_Add(Dqn_FStr8<N> *string, Dqn_Str8 src)
{ {
bool result = Dqn_FStr8_AppendCStr8(string, src.data, src.size); bool result = Dqn_FStr8_AddCStr8(string, src.data, src.size);
return result; return result;
} }

View File

@ -1,127 +0,0 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
//
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
//
// dqn_thread_context.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DQN_THREAD_LOCAL Dqn_ThreadContext g_dqn_thread_context;
// NOTE: [$TCTX] Dqn_ThreadContext /////////////////////////////////////////////////////////////////
Dqn_Scratch::Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index)
{
arena = context->scratch_arenas[context_index];
temp_mem = Dqn_Arena_TempMemBegin(arena);
destructed = false;
}
Dqn_Scratch::~Dqn_Scratch()
{
DQN_ASSERT(destructed == false);
Dqn_Arena_TempMemEnd(temp_mem);
destructed = true;
}
DQN_API bool Dqn_Thread_ContextIsInit()
{
bool result = g_dqn_thread_context.init;
return result;
}
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
{
Dqn_ThreadContext *result = &g_dqn_thread_context;
if (result->init)
return result;
result->init = true;
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
// NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex);
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
// NOTE: We allocate arenas so that they all come from the memory
// allocated from the address space of this library. This allows the
// library to be used across DLL boundaries for example as long as
// across the boundaries the same g_dqn_library instance is shared.
//
// On unload of the DLL, the address space is deallocated. If we stored
// these as TLS stack variables, these arenas would persist and point to
// invalid memory addresses.
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Scratch %zu", Dqn_OS_ThreadID(), index);
// NOTE: Hence here we search for the arena. If it already exists then
// we are in that DLL boundary situation where the TLS data has been
// reinitialised and zero-ed out. We will try and find the matching
// arena in the catalog and re-use it.
//
// NOTE: This operation is so infrequent and the number of arenas one
// has in their program should be low that a string look-up should be
// cheap and fine.
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
if (catalog_item == &catalog->sentinel) {
Dqn_Arena *scratch = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
result->scratch_arenas[index] = scratch;
} else {
// NOTE: Reuse the arena
result->scratch_arenas[index] = catalog_item->arena;
}
}
// NOTE: Setup error sink //////////////////////////////////////////////////////////////////////
{
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Error Sink", Dqn_OS_ThreadID());
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
if (catalog_item == &catalog->sentinel) {
result->error_sink_arena = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
} else {
// NOTE: Reuse the arena
result->error_sink_arena = catalog_item->arena;
}
result->error_sink.arena = result->error_sink_arena;
}
Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex);
return result;
}
// TODO: Is there a way to handle conflict arenas without the user needing to
// manually pass it in?
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena)
{
Dqn_ThreadContext *context = Dqn_ThreadContext_Get();
uint8_t context_index = (uint8_t)-1;
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(context->scratch_arenas); index++) {
Dqn_Arena *arena = context->scratch_arenas[index];
if (!conflict_arena || arena != conflict_arena) {
context_index = index;
break;
}
}
DQN_ASSERT(context_index != (uint8_t)-1);
return Dqn_Scratch(context, context_index);
}

View File

@ -1,52 +0,0 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
//
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
//
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
struct Dqn_ThreadContext
{
Dqn_b32 init;
Dqn_Arena *scratch_arenas[2];
Dqn_Arena *error_sink_arena;
Dqn_CallSite call_site;
Dqn_ErrorSink error_sink;
};
struct Dqn_Scratch
{
Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index);
~Dqn_Scratch();
Dqn_Arena *arena;
Dqn_b32 destructed;
Dqn_ArenaTempMem temp_mem;
};
DQN_API bool Dqn_ThreadContext_IsInit();
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get();
#define Dqn_ThreadContext_SaveCallSite do { Dqn_ThreadContext_Get()->call_site = DQN_CALL_SITE; } while (0)
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena);

126
dqn_tls.cpp Normal file
View File

@ -0,0 +1,126 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$$$$$\
// \__$$ __|$$ | $$ __$$\
// $$ | $$ | $$ / \__|
// $$ | $$ | \$$$$$$\
// $$ | $$ | \____$$\
// $$ | $$ | $$\ $$ |
// $$ | $$$$$$$$\\$$$$$$ |
// \__| \________|\______/
//
// dqn_tls.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
// NOTE: [$TCTX] Dqn_TLS /////////////////////////////////////////////////////////////////
Dqn_TLSTMem::Dqn_TLSTMem(Dqn_TLS *tls, uint8_t arena_index, Dqn_TLSPushTMem push_tmem)
{
DQN_ASSERT(arena_index == Dqn_TLSArena_TMem0 || arena_index == Dqn_TLSArena_TMem1);
arena = tls->arenas + arena_index;
temp_mem = Dqn_Arena_TempMemBegin(arena);
destructed = false;
push_arena = push_tmem;
if (push_arena)
Dqn_TLS_PushArena(arena);
}
Dqn_TLSTMem::~Dqn_TLSTMem()
{
DQN_ASSERT(destructed == false);
Dqn_Arena_TempMemEnd(temp_mem);
destructed = true;
if (push_arena)
Dqn_TLS_PopArena();
}
DQN_API void Dqn_TLS_Init(Dqn_TLS *tls)
{
DQN_CHECK(tls);
if (tls->init)
return;
DQN_FOR_UINDEX (index, Dqn_TLSArena_Count) {
Dqn_Arena *arena = tls->arenas + index;
switch (DQN_CAST(Dqn_TLSArena)index) {
default: *arena = Dqn_Arena_InitSize(DQN_MEGABYTES(4), DQN_KILOBYTES(4), Dqn_ArenaFlag_AllocCanLeak); break;
case Dqn_TLSArena_ErrorSink: *arena = Dqn_Arena_InitSize(DQN_KILOBYTES(64), DQN_KILOBYTES(4), Dqn_ArenaFlag_AllocCanLeak); break;
case Dqn_TLSArena_Count: DQN_INVALID_CODE_PATH; break;
}
}
tls->thread_id = Dqn_OS_ThreadID();
tls->error_sink.arena = tls->arenas + Dqn_TLSArena_ErrorSink;
tls->init = true;
}
DQN_API Dqn_TLS *Dqn_TLS_Get()
{
Dqn_TLS *result = g_dqn_os_thread_tls;
// TODO(doyle): Fix stack-trace infinite loop with requiring the TLS that is not initialised
// yet.
DQN_ASSERT(
g_dqn_library->lib_init &&
"Library context must be be initialised first by calling Dqn_Library_Init. This "
"initialises the main thread's TLS for you (no need to call Dqn_OS_ThreadSetTLS on main)");
DQN_ASSERT(result &&
"Thread must be assigned the TLS with Dqn_OS_ThreadSetTLS. If the library is "
"initialised, then, this thread was created without calling the set TLS function "
"for the spawned thread.");
return result;
}
DQN_API Dqn_Arena *Dqn_TLS_Arena()
{
Dqn_TLS *tls = Dqn_TLS_Get();
Dqn_Arena *result = tls->arenas + Dqn_TLSArena_Main;
return result;
}
// TODO: Is there a way to handle conflict arenas without the user needing to
// manually pass it in?
DQN_API Dqn_TLSTMem Dqn_TLS_GetTMem(void const *conflict_arena, Dqn_TLSPushTMem push_tmem)
{
Dqn_TLS *tls = Dqn_TLS_Get();
uint8_t tls_index = (uint8_t)-1;
for (uint8_t index = Dqn_TLSArena_TMem0; index <= Dqn_TLSArena_TMem1; index++) {
Dqn_Arena *arena = tls->arenas + index;
if (!conflict_arena || arena != conflict_arena) {
tls_index = index;
break;
}
}
DQN_ASSERT(tls_index != (uint8_t)-1);
return Dqn_TLSTMem(tls, tls_index, push_tmem);
}
DQN_API void Dqn_TLS_PushArena(Dqn_Arena *arena)
{
DQN_ASSERT(arena);
Dqn_TLS *tls = Dqn_TLS_Get();
DQN_ASSERT(tls->arena_stack_index < DQN_ARRAY_UCOUNT(tls->arena_stack));
tls->arena_stack[tls->arena_stack_index++] = arena;
}
DQN_API void Dqn_TLS_PopArena()
{
Dqn_TLS *tls = Dqn_TLS_Get();
DQN_ASSERT(tls->arena_stack_index > 0);
tls->arena_stack_index--;
}
DQN_API Dqn_Arena *Dqn_TLS_TopArena()
{
Dqn_TLS *tls = Dqn_TLS_Get();
Dqn_Arena *result = nullptr;
if (tls->arena_stack_index)
result = tls->arena_stack[tls->arena_stack_index - 1];
return result;
}

84
dqn_tls.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
//
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
//
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
enum Dqn_TLSArena
{
Dqn_TLSArena_Main, // NOTE: Arena for Permanent allocations
Dqn_TLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
Dqn_TLSArena_TMem0,
Dqn_TLSArena_TMem1,
Dqn_TLSArena_Count,
};
struct Dqn_TLS
{
Dqn_b32 init; // Flag to track if Thread has been initialised
uint64_t thread_id;
Dqn_CallSite call_site; // Stores call-site information when requested by thread
Dqn_ErrorSink error_sink; // Error handling state
Dqn_Arena arenas[Dqn_TLSArena_Count];
// Push and pop arenas onto the stack. Functions suffixed 'TLS' will use
// these arenas for memory allocation.
Dqn_Arena *arena_stack[8];
Dqn_usize arena_stack_index;
};
// Push the temporary memory arena when retrieved, popped when the arena goes
// out of scope. Pushed arenas are used automatically as the allocator in TLS
// suffixed function.
enum Dqn_TLSPushTMem
{
Dqn_TLSPushTMem_No,
Dqn_TLSPushTMem_Yes,
};
struct Dqn_TLSTMem
{
Dqn_TLSTMem(Dqn_TLS *context, uint8_t context_index, Dqn_TLSPushTMem push_scratch);
~Dqn_TLSTMem();
Dqn_Arena *arena;
Dqn_b32 destructed;
Dqn_TLSPushTMem push_arena;
Dqn_ArenaTempMem temp_mem;
};
DQN_API void Dqn_TLS_Init(Dqn_TLS *tls);
DQN_API Dqn_TLS * Dqn_TLS_Get();
#define Dqn_TLS_SaveCallSite do { Dqn_TLS_Get()->call_site = DQN_CALL_SITE; } while (0)
DQN_API Dqn_TLSTMem Dqn_TLS_GetTMem(void const *conflict_arena, Dqn_TLSPushTMem push_tmp_mem);
#define Dqn_TLS_TMem(...) Dqn_TLS_GetTMem(__VA_ARGS__, Dqn_TLSPushTMem_No)
#define Dqn_TLS_PushTMem(...) Dqn_TLS_GetTMem(__VA_ARGS__, Dqn_TLSPushTMem_Yes)
DQN_API void Dqn_TLS_PushArena(Dqn_Arena *arena);
DQN_API void Dqn_TLS_PopArena();
DQN_API Dqn_Arena * Dqn_TLS_TopArena();

View File

@ -30,6 +30,7 @@ struct Dqn_TypeField
{ {
uint16_t index; uint16_t index;
Dqn_Str8 name; Dqn_Str8 name;
Dqn_Str8 label;
Dqn_isize value; Dqn_isize value;
Dqn_usize offset_of; Dqn_usize offset_of;
Dqn_usize size_of; Dqn_usize size_of;

View File

@ -3,7 +3,7 @@
#include <inttypes.h> #include <inttypes.h>
#if !defined(__clang__)
// NOTE: Taken from MSDN __cpuid example implementation // NOTE: Taken from MSDN __cpuid example implementation
// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170
#include <bitset> #include <bitset>
@ -110,7 +110,7 @@ private:
for (int i = 0; i <= nIds_; ++i) for (int i = 0; i <= nIds_; ++i)
{ {
__cpuidex(cpui.data(), i, 0); __cpuidex(cpui.data(), i, 0);
data_.push_back(cpui); data_[dataSize_++] = cpui;
} }
// Capture vendor string // Capture vendor string
@ -154,7 +154,7 @@ private:
for (int i = 0x80000000; i <= nExIds_; ++i) for (int i = 0x80000000; i <= nExIds_; ++i)
{ {
__cpuidex(cpui.data(), i, 0); __cpuidex(cpui.data(), i, 0);
extdata_.push_back(cpui); extdata_[extdataSize_++] = cpui;
} }
// load bitset with flags for function 0x80000001 // load bitset with flags for function 0x80000001
@ -186,14 +186,15 @@ private:
std::bitset<32> f_7_ECX_; std::bitset<32> f_7_ECX_;
std::bitset<32> f_81_ECX_; std::bitset<32> f_81_ECX_;
std::bitset<32> f_81_EDX_; std::bitset<32> f_81_EDX_;
std::vector<std::array<int, 4>> data_; std::array<std::array<int, 4>, 512> data_{};
std::vector<std::array<int, 4>> extdata_; size_t dataSize_ = 0;
std::array<std::array<int, 4>, 512> extdata_{};
size_t extdataSize_ = 0;
}; };
}; };
// Initialize static member data // Initialize static member data
const Dqn_RefImplCPUReport::Dqn_RefImplCPUReport_Internal Dqn_RefImplCPUReport::CPU_Rep; const Dqn_RefImplCPUReport::Dqn_RefImplCPUReport_Internal Dqn_RefImplCPUReport::CPU_Rep;
#endif // !defined(__clang__)
#if 0 #if 0
static void Dqn_RefImpl_CPUReportDump() // Print out supported instruction set features static void Dqn_RefImpl_CPUReportDump() // Print out supported instruction set features
@ -263,8 +264,6 @@ static Dqn_UTest Dqn_Test_Base()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Base") { DQN_UTEST_GROUP(test, "Dqn_Base") {
// TODO(doyle): cpuid refimpl doesn't work on clang
#if !defined(__clang__)
DQN_UTEST_TEST("Query CPUID") { DQN_UTEST_TEST("Query CPUID") {
Dqn_CPUReport cpu_report = Dqn_CPU_Report(); Dqn_CPUReport cpu_report = Dqn_CPU_Report();
@ -324,8 +323,8 @@ static Dqn_UTest Dqn_Test_Base()
DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XOP) == Dqn_RefImplCPUReport::XOP()); DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XOP) == Dqn_RefImplCPUReport::XOP());
DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XSAVE) == Dqn_RefImplCPUReport::XSAVE()); DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XSAVE) == Dqn_RefImplCPUReport::XSAVE());
#endif #endif
} }
#endif // !defined(__clang__)
} }
return test; return test;
} }
@ -427,7 +426,7 @@ static Dqn_UTest Dqn_Test_Arena()
static Dqn_UTest Dqn_Test_Bin() static Dqn_UTest Dqn_Test_Bin()
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Bin") { DQN_UTEST_GROUP(test, "Dqn_Bin") {
DQN_UTEST_TEST("Convert 0x123") { DQN_UTEST_TEST("Convert 0x123") {
@ -487,23 +486,23 @@ static Dqn_UTest Dqn_Test_Bin()
uint32_t number = 0xd095f6; uint32_t number = 0xd095f6;
DQN_UTEST_TEST("Convert %x to string", number) { DQN_UTEST_TEST("Convert %x to string", number) {
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number)); Dqn_Str8 number_hex = Dqn_BytesToHex(tmem.arena, &number, sizeof(number));
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex)); DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
} }
number = 0xf6ed00; number = 0xf6ed00;
DQN_UTEST_TEST("Convert %x to string", number) { DQN_UTEST_TEST("Convert %x to string", number) {
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number)); Dqn_Str8 number_hex = Dqn_BytesToHex(tmem.arena, &number, sizeof(number));
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("00edf600")), "number_hex=%.*s", DQN_STR_FMT(number_hex)); DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("00edf600")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
} }
Dqn_Str8 hex = DQN_STR8("0xf6ed00"); Dqn_Str8 hex = DQN_STR8("0xf6ed00");
DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) { DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) {
Dqn_Str8 bytes = Dqn_HexToBytes(scratch.arena, hex); Dqn_Str8 bytes = Dqn_HexToBytes(tmem.arena, hex);
DQN_UTEST_ASSERTF(&test, DQN_UTEST_ASSERTF(&test,
Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")), Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")),
"number_hex=%.*s", "number_hex=%.*s",
DQN_STR_FMT(Dqn_BytesToHex(scratch.arena, bytes.data, bytes.size))); DQN_STR_FMT(Dqn_BytesToHex(tmem.arena, bytes.data, bytes.size)));
} }
} }
@ -822,11 +821,11 @@ static Dqn_UTest Dqn_Test_DSMap()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_DSMap") { DQN_UTEST_GROUP(test, "Dqn_DSMap") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
{ {
Dqn_Arena arena = {}; Dqn_Arena arena = {};
uint32_t const MAP_SIZE = 64; uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE); Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil);
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); }; DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("Find non-existent value") { DQN_UTEST_TEST("Find non-existent value") {
@ -867,10 +866,10 @@ static Dqn_UTest Dqn_Test_DSMap()
case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break; case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break;
} }
Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(scratch.arena); Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(tmem.arena);
Dqn_Arena arena = {}; Dqn_Arena arena = {};
uint32_t const MAP_SIZE = 64; uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE); Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil);
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); }; DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) { DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) {
@ -878,8 +877,7 @@ static Dqn_UTest Dqn_Test_DSMap()
uint64_t value = 0; uint64_t value = 0;
uint64_t grow_threshold = map_start_size * 3 / 4; uint64_t grow_threshold = map_start_size * 3 / 4;
for (; map.occupied != grow_threshold; value++) { for (; map.occupied != grow_threshold; value++) {
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
Dqn_DSMapResult<uint64_t> make_result = {}; Dqn_DSMapResult<uint64_t> make_result = {};
if (test_type == DSMapTestType_Set) { if (test_type == DSMapTestType_Set) {
@ -895,8 +893,7 @@ static Dqn_UTest Dqn_Test_DSMap()
DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value); DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value);
{ // NOTE: One more item should cause the table to grow by 2x { // NOTE: One more item should cause the table to grow by 2x
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
Dqn_DSMapResult<uint64_t> make_result = {}; Dqn_DSMapResult<uint64_t> make_result = {};
if (test_type == DSMapTestType_Set) { if (test_type == DSMapTestType_Set) {
make_result = Dqn_DSMap_Set(&map, key, value); make_result = Dqn_DSMap_Set(&map, key, value);
@ -924,7 +921,7 @@ static Dqn_UTest Dqn_Test_DSMap()
// NOTE: Validate each slot value // NOTE: Validate each slot value
uint64_t value_test = index - 1; uint64_t value_test = index - 1;
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, &value_test, sizeof(value_test)); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value_test);
DQN_UTEST_ASSERT(&test, Dqn_DSMap_KeyEquals(slot->key, key)); DQN_UTEST_ASSERT(&test, Dqn_DSMap_KeyEquals(slot->key, key));
if (test_type == DSMapTestType_Set) { if (test_type == DSMapTestType_Set) {
DQN_UTEST_ASSERT(&test, slot->value == value_test); DQN_UTEST_ASSERT(&test, slot->value == value_test);
@ -945,9 +942,7 @@ static Dqn_UTest Dqn_Test_DSMap()
uint64_t value = 0; uint64_t value = 0;
uint64_t shrink_threshold = map.size * 1 / 4; uint64_t shrink_threshold = map.size * 1 / 4;
for (; map.occupied != shrink_threshold; value++) { for (; map.occupied != shrink_threshold; value++) {
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value);
Dqn_DSMap_Erase(&map, key); Dqn_DSMap_Erase(&map, key);
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
@ -955,9 +950,8 @@ static Dqn_UTest Dqn_Test_DSMap()
DQN_UTEST_ASSERT(&test, map.size == start_map_size); DQN_UTEST_ASSERT(&test, map.size == start_map_size);
DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value); DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value);
{ // NOTE: One more item should cause the table to grow by 2x { // NOTE: One more item should cause the table to shrink by 2x
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
Dqn_DSMap_Erase(&map, key); Dqn_DSMap_Erase(&map, key);
value++; value++;
@ -971,12 +965,12 @@ static Dqn_UTest Dqn_Test_DSMap()
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(&sentinel, &NIL_SLOT, sizeof(NIL_SLOT)) == 0); DQN_UTEST_ASSERT(&test, DQN_MEMCMP(&sentinel, &NIL_SLOT, sizeof(NIL_SLOT)) == 0);
} }
// NOTE: Recheck all the hash table values after growing // NOTE: Recheck all the hash table values after shrinking
for (uint64_t index = 1 /*Sentinel*/; index < map.occupied; index++) { for (uint64_t index = 1 /*Sentinel*/; index < map.occupied; index++) {
// NOTE: Generate the key // NOTE: Generate the key
uint64_t value_test = value + (index - 1); uint64_t value_test = value + (index - 1);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)&value_test, sizeof(value_test)); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value_test);
// NOTE: Validate each slot value // NOTE: Validate each slot value
Dqn_DSMapResult<uint64_t> find_result = Dqn_DSMap_Find(&map, key); Dqn_DSMapResult<uint64_t> find_result = Dqn_DSMap_Find(&map, key);
@ -995,8 +989,7 @@ static Dqn_UTest Dqn_Test_DSMap()
} }
for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value);
Dqn_DSMap_Erase(&map, key); Dqn_DSMap_Erase(&map, key);
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
@ -1016,12 +1009,12 @@ static Dqn_UTest Dqn_Test_FStr8()
DQN_UTEST_GROUP(test, "Dqn_FStr8") { DQN_UTEST_GROUP(test, "Dqn_FStr8") {
DQN_UTEST_TEST("Append too much fails") { DQN_UTEST_TEST("Append too much fails") {
Dqn_FStr8<4> str = {}; Dqn_FStr8<4> str = {};
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_Append(&str, DQN_STR8("abcde"))); DQN_UTEST_ASSERT(&test, !Dqn_FStr8_Add(&str, DQN_STR8("abcde")));
} }
DQN_UTEST_TEST("Append format string too much fails") { DQN_UTEST_TEST("Append format string too much fails") {
Dqn_FStr8<4> str = {}; Dqn_FStr8<4> str = {};
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AppendF(&str, "abcde")); DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AddF(&str, "abcde"));
} }
} }
return test; return test;
@ -1049,8 +1042,8 @@ static Dqn_UTest Dqn_Test_Fs()
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE)); DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
// NOTE: Read step // NOTE: Read step
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena, nullptr); Dqn_Str8 read_file = Dqn_OS_ReadAll(tmem.arena, SRC_FILE, nullptr);
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file"); DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes"); DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file)); DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
@ -1301,8 +1294,8 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] =
void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 input_hex = Dqn_BytesToHex(scratch.arena, input.data, input.size); Dqn_Str8 input_hex = Dqn_BytesToHex(tmem.arena, input.data, input.size);
switch(hash_type) switch(hash_type)
{ {
@ -1536,8 +1529,8 @@ static Dqn_UTest Dqn_Test_OS()
} }
DQN_UTEST_TEST("Query executable directory") { DQN_UTEST_TEST("Query executable directory") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena); Dqn_Str8 result = Dqn_OS_EXEDir(tmem.arena);
DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result)); DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result));
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result)); DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
} }
@ -1709,8 +1702,8 @@ static Dqn_UTest Dqn_Test_Str8()
} }
DQN_UTEST_TEST("Initialise with format string") { DQN_UTEST_TEST("Initialise with format string") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_Str8_InitF(scratch.arena, "%s", "AB"); Dqn_Str8 string = Dqn_Str8_InitF(tmem.arena, "%s", "AB");
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size); DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size);
DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]); DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]);
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
@ -1718,9 +1711,9 @@ static Dqn_UTest Dqn_Test_Str8()
} }
DQN_UTEST_TEST("Copy string") { DQN_UTEST_TEST("Copy string") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = DQN_STR8("AB"); Dqn_Str8 string = DQN_STR8("AB");
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, string); Dqn_Str8 copy = Dqn_Str8_Copy(tmem.arena, string);
DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %zu", copy.size); DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %zu", copy.size);
DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]); DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]);
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]); DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
@ -1733,8 +1726,8 @@ static Dqn_UTest Dqn_Test_Str8()
} }
DQN_UTEST_TEST("Allocate string from arena") { DQN_UTEST_TEST("Allocate string from arena") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No); Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, 2, Dqn_ZeroMem_No);
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size); DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size);
} }
@ -1950,7 +1943,7 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_TEST("Find: String (char) is not in buffer") { DQN_UTEST_TEST("Find: String (char) is not in buffer") {
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
Dqn_Str8 find = DQN_STR8("2"); Dqn_Str8 find = DQN_STR8("2");
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find); Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find, Dqn_Str8EqCase_Sensitive);
DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, !result.found);
DQN_UTEST_ASSERT(&test, result.index == 0); DQN_UTEST_ASSERT(&test, result.index == 0);
DQN_UTEST_ASSERT(&test, result.match.data == nullptr); DQN_UTEST_ASSERT(&test, result.match.data == nullptr);
@ -1960,7 +1953,7 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_TEST("Find: String (char) is in buffer") { DQN_UTEST_TEST("Find: String (char) is in buffer") {
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
Dqn_Str8 find = DQN_STR8("6"); Dqn_Str8 find = DQN_STR8("6");
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find); Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find, Dqn_Str8EqCase_Sensitive);
DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.found);
DQN_UTEST_ASSERT(&test, result.index == 2); DQN_UTEST_ASSERT(&test, result.index == 2);
DQN_UTEST_ASSERT(&test, result.match.data[0] == '6'); DQN_UTEST_ASSERT(&test, result.match.data[0] == '6');
@ -2175,23 +2168,23 @@ static Dqn_UTest Dqn_Test_Win()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "OS Win32") { DQN_UTEST_GROUP(test, "OS Win32") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
Dqn_Str8 input8 = DQN_STR8("String"); Dqn_Str8 input8 = DQN_STR8("String");
Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1}; Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1};
DQN_UTEST_TEST("Str8 to Str16") { DQN_UTEST_TEST("Str8 to Str16") {
Dqn_Str16 result = Dqn_Win_Str8ToStr16(scratch.arena, input8); Dqn_Str16 result = Dqn_Win_Str8ToStr16(tmem.arena, input8);
DQN_UTEST_ASSERT(&test, result == input16); DQN_UTEST_ASSERT(&test, result == input16);
} }
DQN_UTEST_TEST("Str16 to Str8") { DQN_UTEST_TEST("Str16 to Str8") {
Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, input16); Dqn_Str8 result = Dqn_Win_Str16ToStr8(tmem.arena, input16);
DQN_UTEST_ASSERT(&test, result == input8); DQN_UTEST_ASSERT(&test, result == input8);
} }
DQN_UTEST_TEST("Str16 to Str8: Null terminates string") { DQN_UTEST_TEST("Str16 to Str8: Null terminates string") {
int size_required = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0); int size_required = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No); char *string = Dqn_Arena_NewArray(tmem.arena, char, size_required + 1, Dqn_ZeroMem_No);
// Fill the string with error sentinels // Fill the string with error sentinels
DQN_MEMSET(string, 'Z', size_required + 1); DQN_MEMSET(string, 'Z', size_required + 1);
@ -2205,7 +2198,7 @@ static Dqn_UTest Dqn_Test_Win()
} }
DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") { DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") {
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, input16); Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(tmem.arena, input16);
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0); int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
@ -2259,7 +2252,7 @@ void Dqn_Test_RunSuite()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
(void)argv; (void)argc; (void)argv; (void)argc;
Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures); Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures);
Dqn_Test_RunSuite(); Dqn_Test_RunSuite();
return 0; return 0;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "dqn.h" #include "dqn.h"
/*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
@ -15,11 +16,15 @@
// dqn_win32.h -- Windows replacement header // dqn_win32.h -- Windows replacement header
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
*/
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#pragma comment(lib, "bcrypt") #pragma comment(lib, "bcrypt")
#pragma comment(lib, "winhttp") #pragma comment(lib, "winhttp")
#pragma comment(lib, "dbghelp") #pragma comment(lib, "dbghelp")
#pragma comment(lib, "comdlg32")
#pragma comment(lib, "pathcch")
#pragma comment(lib, "shlwapi")
#endif #endif
#if defined(DQN_NO_WIN32_MIN_HEADER) || defined(_INC_WINDOWS) #if defined(DQN_NO_WIN32_MIN_HEADER) || defined(_INC_WINDOWS)
@ -27,6 +32,8 @@
#include <Windows.h> // LONG #include <Windows.h> // LONG
#include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc #include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc #include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
#include <shlwapi.h> // PathRelativePathTO
#include <pathcch.h> // PathCchCanonicalizeEx
#include <winhttp.h> // WinHttp* #include <winhttp.h> // WinHttp*
#include <DbgHelp.h> #include <DbgHelp.h>
#else #else
@ -35,6 +42,7 @@
// NOTE: basetsd.h ///////////////////////////////////////////////////////////////////////////// // NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
typedef ULONG_PTR SIZE_T, *PSIZE_T; typedef ULONG_PTR SIZE_T, *PSIZE_T;
typedef __int64 LONG_PTR, *PLONG_PTR; typedef __int64 LONG_PTR, *PLONG_PTR;
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
@ -47,17 +55,21 @@
}; };
typedef struct HINSTANCE__ *HINSTANCE; typedef struct HINSTANCE__ *HINSTANCE;
typedef unsigned long DWORD; typedef unsigned long DWORD;
typedef int BOOL; typedef int BOOL;
typedef int INT; typedef int INT;
typedef unsigned long ULONG; typedef unsigned long ULONG;
typedef unsigned int UINT; typedef unsigned int UINT;
typedef unsigned short WORD; typedef unsigned short WORD;
typedef unsigned char BYTE; typedef unsigned char BYTE;
typedef unsigned char UCHAR; typedef unsigned char UCHAR;
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
typedef void * HANDLE; typedef void * HANDLE;
typedef HANDLE HLOCAL; typedef HANDLE HLOCAL;
typedef unsigned __int64 WPARAM;
typedef LONG_PTR LPARAM;
typedef LONG_PTR LRESULT;
#define MAX_PATH 260 #define MAX_PATH 260
@ -85,6 +97,7 @@
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef CHAR * NPSTR, *LPSTR, *PSTR; typedef CHAR * NPSTR, *LPSTR, *PSTR;
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR; typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
typedef long HRESULT;
// NOTE: VirtualAlloc: Allocation Type // NOTE: VirtualAlloc: Allocation Type
#define MEM_RESERVE 0x00002000 #define MEM_RESERVE 0x00002000
@ -130,6 +143,9 @@
#define STATUS_WAIT_0 ((DWORD )0x00000000L) #define STATUS_WAIT_0 ((DWORD )0x00000000L)
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) #define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
#define S_OK ((HRESULT)0L)
#define S_FALSE ((HRESULT)1L)
typedef union _ULARGE_INTEGER { typedef union _ULARGE_INTEGER {
struct { struct {
DWORD LowPart; DWORD LowPart;
@ -1047,12 +1063,11 @@
extern "C" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall CreateProcessW(WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation); __declspec(dllimport) BOOL __stdcall CreateProcessW (WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation);
__declspec(dllimport) HANDLE __stdcall CreateThread(SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId); __declspec(dllimport) HANDLE __stdcall CreateThread (SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId);
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID); __declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID);
__declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode); __declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode);
__declspec(dllimport) void __stdcall ExitProcess(UINT uExitCode); __declspec(dllimport) void __stdcall ExitProcess (UINT uExitCode);
} }
// NOTE: um/memoryapi.h //////////////////////////////////////////////////////////////////////// // NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
@ -1077,6 +1092,7 @@
extern "C" extern "C"
{ {
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd); __declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteW(HWND hwnd, WCHAR const *lpOperation, WCHAR const *lpFile, WCHAR const *lpParameters, WCHAR const *lpDirectory, INT nShowCmd);
} }
// NOTE: um/debugapi.h ///////////////////////////////////////////////////////////////////////// // NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
@ -1088,14 +1104,150 @@
// NOTE: um/namedpipeapi.h ///////////////////////////////////////////////////////////////////// // NOTE: um/namedpipeapi.h /////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize); __declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
__declspec(dllimport) BOOL __stdcall PeekNamedPipe(HANDLE hNamedPipe, VOID *lpBuffer, DWORD nBufferSize, DWORD *lpBytesRead, DWORD *lpTotalBytesAvail, DWORD *lpBytesLeftThisMessage);
} }
// NOTE: um/handleapi.h ///////////////////////////////////////////////////////////////////// // NOTE: um/handleapi.h ////////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags); __declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
} }
// NOTE: um/commdlg.h //////////////////////////////////////////////////////////////////////////
typedef UINT_PTR (__stdcall *LPOFNHOOKPROC)(HWND, UINT, WPARAM, LPARAM);
typedef struct tagOFNW {
DWORD lStructSize;
HWND hwndOwner;
HINSTANCE hInstance;
WCHAR const * lpstrFilter;
LPWSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPWSTR lpstrFile;
DWORD nMaxFile;
LPWSTR lpstrFileTitle;
DWORD nMaxFileTitle;
WCHAR const * lpstrInitialDir;
WCHAR const * lpstrTitle;
DWORD Flags;
WORD nFileOffset;
WORD nFileExtension;
WCHAR const * lpstrDefExt;
LPARAM lCustData;
LPOFNHOOKPROC lpfnHook;
WCHAR const * lpTemplateName;
#ifdef _MAC
LPEDITMENU lpEditInfo;
LPCSTR lpstrPrompt;
#endif
#if (_WIN32_WINNT >= 0x0500)
void * pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
#endif // (_WIN32_WINNT >= 0x0500)
} OPENFILENAMEW, *LPOPENFILENAMEW;
#define OFN_READONLY 0x00000001
#define OFN_OVERWRITEPROMPT 0x00000002
#define OFN_HIDEREADONLY 0x00000004
#define OFN_NOCHANGEDIR 0x00000008
#define OFN_SHOWHELP 0x00000010
#define OFN_ENABLEHOOK 0x00000020
#define OFN_ENABLETEMPLATE 0x00000040
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
#define OFN_NOVALIDATE 0x00000100
#define OFN_ALLOWMULTISELECT 0x00000200
#define OFN_EXTENSIONDIFFERENT 0x00000400
#define OFN_PATHMUSTEXIST 0x00000800
#define OFN_FILEMUSTEXIST 0x00001000
#define OFN_CREATEPROMPT 0x00002000
#define OFN_SHAREAWARE 0x00004000
#define OFN_NOREADONLYRETURN 0x00008000
#define OFN_NOTESTFILECREATE 0x00010000
#define OFN_NONETWORKBUTTON 0x00020000
#define OFN_NOLONGNAMES 0x00040000 // force no long names for 4.x modules
#if(WINVER >= 0x0400)
#define OFN_EXPLORER 0x00080000 // new look commdlg
#define OFN_NODEREFERENCELINKS 0x00100000
#define OFN_LONGNAMES 0x00200000 // force long names for 3.x modules
// OFN_ENABLEINCLUDENOTIFY and OFN_ENABLESIZING require
// Windows 2000 or higher to have any effect.
#define OFN_ENABLEINCLUDENOTIFY 0x00400000 // send include message to callback
#define OFN_ENABLESIZING 0x00800000
#endif /* WINVER >= 0x0400 */
#if (_WIN32_WINNT >= 0x0500)
#define OFN_DONTADDTORECENT 0x02000000
#define OFN_FORCESHOWHIDDEN 0x10000000 // Show All files including System and hidden files
#endif // (_WIN32_WINNT >= 0x0500)
//FlagsEx Values
#if (_WIN32_WINNT >= 0x0500)
#define OFN_EX_NOPLACESBAR 0x00000001
#endif // (_WIN32_WINNT >= 0x0500)
extern "C"
{
__declspec(dllimport) BOOL __stdcall GetSaveFileNameW(LPOPENFILENAMEW);
__declspec(dllimport) BOOL __stdcall GetOpenFileNameW(LPOPENFILENAMEW);
}
// NOTE: um/shlwapi.h //////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) BOOL __stdcall PathRelativePathToW(WCHAR *pszPath, WCHAR const *pszFrom, DWORD dwAttrFrom, WCHAR const *pszTo, DWORD dwAttrTo);
__declspec(dllimport) BOOL __stdcall PathIsRelativeW(WCHAR *pszPath);
}
// NOTE: um/pathcch.h //////////////////////////////////////////////////////////////////////////
typedef enum PATHCCH_OPTIONS
{
PATHCCH_NONE = 0x0,
// This option allows applications to gain access to long paths. It has two
// different behaviors. For process configured to enable long paths it will allow
// the returned path to be longer than the max path limit that is normally imposed.
// For process that are not this option will convert long paths into the extended
// length DOS device form (with \\?\ prefix) when the path is longer than the limit.
// This form is not length limited by the Win32 file system API on all versions of Windows.
// This second behavior is the same behavior for OSes that don't have the long path feature.
// This can not be specified with PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH.
PATHCCH_ALLOW_LONG_PATHS = 0x01,
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
// Forces the API to treat the caller as long path enabled, independent of the
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS.
PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS = 0x02,
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
// Forces the API to treat the caller as long path disabled, independent of the
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS.
PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS = 0x04,
// Disable the normalization of path segments that includes removing trailing dots and spaces.
// This enables access to paths that win32 path normalization will block.
PATHCCH_DO_NOT_NORMALIZE_SEGMENTS = 0x08,
// Convert the input path into the extended length DOS device path form (with the \\?\ prefix)
// if not already in that form. This enables access to paths that are otherwise not addressable
// due to Win32 normalization rules (that can strip trailing dots and spaces) and path
// length limitations. This option implies the same behavior of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS.
// This can not be specified with PATHCCH_ALLOW_LONG_PATHS.
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH = 0x10,
// When combining or normalizing a path ensure there is a trailing backslash.
PATHCCH_ENSURE_TRAILING_SLASH = 0x020,
// Convert forward slashes to back slashes and collapse multiple slashes.
// This is needed to to support sub-path or identity comparisons.
PATHCCH_CANONICALIZE_SLASHES = 0x040,
} PATHCCH_OPTIONS;
extern "C"
{
__declspec(dllimport) HRESULT __stdcall PathCchCanonicalizeEx(PWSTR pszPathOut, size_t cchPathOut, WCHAR const *pszPathIn, ULONG dwFlags);
};
DQN_MSVC_WARNING_POP DQN_MSVC_WARNING_POP
#endif // !defined(_INC_WINDOWS) #endif // !defined(_INC_WINDOWS)