From 6b403eae717894a08126f292934317aa77ebe42c Mon Sep 17 00:00:00 2001 From: doylet Date: Thu, 1 Aug 2024 13:34:36 +1000 Subject: [PATCH] Pull in changes from internal project --- Standalone/dqn_cpp_file.h | 2 + build.bat | 2 +- dqn.cpp | 14 +- dqn.h | 11 +- dqn_allocator.cpp | 191 ++++++++++---- dqn_allocator.h | 51 +++- dqn_base.cpp | 49 ++-- dqn_base.h | 17 +- dqn_cgen.cpp | 517 ++++++++++++++++++++++++++++---------- dqn_cgen.h | 4 + dqn_containers.cpp | 56 ++++- dqn_containers.h | 312 ++++++++++++++--------- dqn_debug.cpp | 28 +-- dqn_docs.cpp | 138 ++++++---- dqn_helpers.cpp | 219 +++++++++------- dqn_helpers.h | 136 ++++++++-- dqn_json.h | 2 +- dqn_math.cpp | 8 +- dqn_os.cpp | 79 ++++-- dqn_os.h | 80 +++--- dqn_os_posix.cpp | 119 ++++++--- dqn_os_win32.cpp | 273 +++++++++++--------- dqn_os_win32.h | 2 + dqn_string.cpp | 261 +++++++++++++++---- dqn_string.h | 200 +++++++++------ dqn_thread_context.cpp | 127 ---------- dqn_thread_context.h | 52 ---- dqn_tls.cpp | 126 ++++++++++ dqn_tls.h | 84 +++++++ dqn_type_info.h | 1 + dqn_unit_tests.cpp | 103 ++++---- dqn_win32.h | 188 ++++++++++++-- 32 files changed, 2317 insertions(+), 1135 deletions(-) delete mode 100644 dqn_thread_context.cpp delete mode 100644 dqn_thread_context.h create mode 100644 dqn_tls.cpp create mode 100644 dqn_tls.h diff --git a/Standalone/dqn_cpp_file.h b/Standalone/dqn_cpp_file.h index 6f67354..15efdc2 100644 --- a/Standalone/dqn_cpp_file.h +++ b/Standalone/dqn_cpp_file.h @@ -1,3 +1,4 @@ +/* //////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\ @@ -12,6 +13,7 @@ // dqn_cpp_file.h -- Functions to emit C++ formatted code // //////////////////////////////////////////////////////////////////////////////// +*/ #if !defined(DQN_CPP_FILE_H) #define DQN_CPP_FILE_H diff --git a/build.bat b/build.bat index 7569fba..cf97194 100644 --- a/build.bat +++ b/build.bat @@ -13,7 +13,7 @@ pushd Build REM O2 Optimisation Level 2 REM Oi Use CPU Intrinsics 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 diff --git a/dqn.cpp b/dqn.cpp index 2e9c258..60eb796 100644 --- a/dqn.cpp +++ b/dqn.cpp @@ -21,7 +21,17 @@ #if !defined(DQN_NO_METADESK) DQN_MSVC_WARNING_PUSH DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '': 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" + + DQN_GCC_WARNING_POP DQN_MSVC_WARNING_POP #endif #define DQN_CPP_FILE_IMPLEMENTATION @@ -34,13 +44,13 @@ #endif #include "dqn_base.cpp" -#include "dqn_thread_context.cpp" #include "dqn_external.cpp" #include "dqn_allocator.cpp" #include "dqn_debug.cpp" #include "dqn_string.cpp" #include "dqn_containers.cpp" #include "dqn_type_info.cpp" +#include "dqn_os.cpp" #if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64) #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 #endif -#include "dqn_os.cpp" +#include "dqn_tls.cpp" #include "dqn_math.cpp" #include "dqn_hash.cpp" #include "dqn_helpers.cpp" diff --git a/dqn.h b/dqn.h index 7a52806..f469835 100644 --- a/dqn.h +++ b/dqn.h @@ -126,7 +126,7 @@ // // 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 -// 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 // @@ -300,17 +300,14 @@ #define _CRT_SECURE_NO_WARNINGS #define DQN_UNDO_CRT_SECURE_NO_WARNINGS #endif + #define MD_DEFAULT_SPRINTF 0 + #define MD_IMPL_Vsnprintf DQN_VSNPRINTF #include "External/metadesk/md.h" #if defined(DQN_UNDO_CRT_SECURE_NO_WARNINGS) #undef _CRT_SECURE_NO_WARNINGS #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 #define DQN_NO_WIN32_MIN_HEADER #endif @@ -321,7 +318,7 @@ #include "dqn_win32.h" #endif #include "dqn_allocator.h" -#include "dqn_thread_context.h" +#include "dqn_tls.h" #include "dqn_debug.h" #include "dqn_string.h" #include "dqn_containers.h" diff --git a/dqn_allocator.cpp b/dqn_allocator.cpp index 74295d3..aabc222 100644 --- a/dqn_allocator.cpp +++ b/dqn_allocator.cpp @@ -56,11 +56,28 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInitFlags(uint64_t reserve, uint64_t comm 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_Arena result = {}; result.flags = flags; result.curr = Dqn_Arena_BlockInitFlags(reserve, commit, flags); + Dqn_Arena_UpdateStatsOnNewBlock_(&result, result.curr); return result; } @@ -123,8 +140,10 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq if (!arena) return nullptr; - if (!arena->curr) + if (!arena->curr) { arena->curr = Dqn_Arena_BlockInitFlags(DQN_ARENA_RESERVE_SIZE, DQN_ARENA_COMMIT_SIZE, arena->flags); + Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr); + } if (!arena->curr) 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; arena->curr = new_block; new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve; + Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr); 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; if (poison) 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; - curr->used = end_pos; + void *result = DQN_CAST(char *) curr + offset_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); 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_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; } @@ -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) { - if (!arena) + if (!arena || !arena->curr) return; uint64_t used = DQN_MAX(DQN_ARENA_HEADER_SIZE, init_used); Dqn_ArenaBlock *curr = arena->curr; while (curr->reserve_sum >= used) { 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; Dqn_Arena_BlockDeinit_(arena, block_to_free); } - arena->curr = curr; - curr->used = used - curr->reserve_sum; - char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT); - Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr; + arena->stats.info.used -= curr->used; + arena->curr = curr; + curr->used = used - curr->reserve_sum; + 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); + arena->stats.info.used += curr->used; } 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; 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 end = begin + block->reserve; 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; } + +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_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 slot_index = 0; 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. + Dqn_usize dist_to_next_msb = Dqn_CountLeadingZerosU64(required_size) + 1; dist_to_next_msb -= DQN_CAST(Dqn_usize)(!Dqn_IsPowerOfTwo(required_size)); 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) { - if (!Dqn_ChunkPool_IsValid(pool)) + if (!Dqn_ChunkPool_IsValid(pool) || !ptr) return; - Dqn_usize offset_to_original_ptr = 0; - DQN_MEMCPY(&offset_to_original_ptr, &(DQN_CAST(char *)ptr)[-1], 1); + DQN_ASSERT(Dqn_Arena_OwnsPtr(pool->arena, ptr)); + + 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); - 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_ChunkPoolSlotSize slot_index = DQN_CAST(Dqn_ChunkPoolSlotSize)(DQN_CAST(uintptr_t)slot->next); 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; } +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 ////////////////////////////////////////////////////////////////// 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; } -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 // 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) { result->arena = arena; result->label = label; + result->arena_pool_allocated = arena_pool_allocated; // NOTE: Add to the catalog (linked list) 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_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, ...) { 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_TicketMutex_End(&catalog->ticket_mutex); 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) @@ -487,35 +563,18 @@ DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_TicketMutex_End(&catalog->ticket_mutex); - Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); -} - -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_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/); } 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_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_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; } @@ -525,8 +584,34 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize va_start(args, fmt); Dqn_TicketMutex_Begin(&catalog->ticket_mutex); 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_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label); 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; } diff --git a/dqn_allocator.h b/dqn_allocator.h index 0fdeed1..7c50b7c 100644 --- a/dqn_allocator.h +++ b/dqn_allocator.h @@ -50,11 +50,26 @@ enum Dqn_ArenaFlag 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 { 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. + uint8_t flags; }; struct Dqn_ArenaTempMem @@ -131,6 +146,7 @@ struct Dqn_ArenaCatalogItem { Dqn_Arena *arena; Dqn_Str8 label; + bool arena_pool_allocated; Dqn_ArenaCatalogItem *next; Dqn_ArenaCatalogItem *prev; }; @@ -143,6 +159,12 @@ struct Dqn_ArenaCatalog uint16_t arena_count; }; +enum Dqn_ArenaCatalogFreeArena +{ + Dqn_ArenaCatalogFreeArena_No, + Dqn_ArenaCatalogFreeArena_Yes, +}; + // NOTE: [$AREN] Dqn_Arena ///////////////////////////////////////////////////////////////////////// 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); @@ -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 void Dqn_Arena_Clear (Dqn_Arena *arena); 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 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) @@ -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)) // 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 bool Dqn_ChunkPool_IsValid (Dqn_ChunkPool const *pool); 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_AllocStr8Copy (Dqn_ChunkPool *pool, Dqn_Str8 string); 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 ////////////////////////////////////////////////////////////////// -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 void Dqn_ArenaCatalog_AddLabelRef (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label); -DQN_API void Dqn_ArenaCatalog_AddLabelCopy (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_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args); -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 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, ...); +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 void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); +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_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, ...); +DQN_API bool Dqn_ArenaCatalog_Erase (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena); diff --git a/dqn_base.cpp b/dqn_base.cpp index 9a2a5a0..1125697 100644 --- a/dqn_base.cpp +++ b/dqn_base.cpp @@ -77,8 +77,8 @@ DQN_API void Dqn_CPU_SetFeature(Dqn_CPUReport *report, Dqn_CPUFeature feature) DQN_API Dqn_CPUReport Dqn_CPU_Report() { Dqn_CPUReport result = {}; - Dqn_CPUIDResult fn_0000_[16] = {}; - Dqn_CPUIDResult fn_8000_[64] = {}; + Dqn_CPUIDResult fn_0000_[512] = {}; + Dqn_CPUIDResult fn_8000_[512] = {}; int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000; 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 ///////////////////////////// { - DQN_ASSERT((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_)); + DQN_ASSERTF((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_), + "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++) { 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 ////////////////////////// Dqn_TicketMutex_Begin(&lib->log_file_mutex); if (lib->log_to_file && !lib->log_file.handle && !lib->log_file.error) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.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); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + 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); } Dqn_TicketMutex_End(&lib->log_file_mutex); // NOTE: Generate the log header /////////////////////////////////////////// - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 log_line = Dqn_Log_MakeStr8(t_mem.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args); // NOTE: Print log ///////////////////////////////////////////////////////// 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 ///////////////////////////////////////////////////////////////////// DQN_API Dqn_ErrorSink *Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode mode) { - Dqn_ThreadContext *thread_context = Dqn_ThreadContext_Get(); - Dqn_ErrorSink *result = &thread_context->error_sink; - Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes); - node->next = result->stack; - node->arena_pos = Dqn_Arena_Pos(result->arena); - node->mode = mode; - result->stack = node; + Dqn_TLS *tls = Dqn_TLS_Get(); + Dqn_ErrorSink *result = &tls->error_sink; + Dqn_usize arena_pos = Dqn_Arena_Pos(result->arena); + Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes); + node->next = result->stack; + node->arena_pos = arena_pos; + node->mode = mode; + result->stack = node; 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(scratch.arena, error); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(t_mem.arena, error); if (node.error) { 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)); @@ -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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args); bool result = Dqn_ErrorSink_EndAndLogError(error, log); return result; } @@ -665,8 +668,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogErrorF(Dqn_ErrorSink *error, DQN_FMT_ATTRIB { va_list args; va_start(args, fmt); - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args); bool result = Dqn_ErrorSink_EndAndLogError(error, log); va_end(args); 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->error_code = error_code; 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) Dqn_ErrorSink_EndAndExitIfErrorF(error, error_code, "Fatal error %u", error_code); } diff --git a/dqn_base.h b/dqn_base.h index d3d0751..00d5d34 100644 --- a/dqn_base.h +++ b/dqn_base.h @@ -145,16 +145,16 @@ #if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE) #include #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 #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 #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 #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 @@ -424,6 +424,9 @@ struct Dqn_ErrorSink #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_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value) + + #define Dqn_CountLeadingZerosU64(value) __lzcnt64(value) + #define Dqn_CPU_TSC() __rdtsc() #define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence() #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_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_CountLeadingZerosU64(value) __builtin_clzll(value) #if defined(DQN_COMPILER_GCC) #define Dqn_CPU_TSC() __rdtsc() #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_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_MakeF(error, error_code, fmt, ...) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_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_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_MakeF_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...); diff --git a/dqn_cgen.cpp b/dqn_cgen.cpp index e663ded..6de2a4b 100644 --- a/dqn_cgen.cpp +++ b/dqn_cgen.cpp @@ -1,3 +1,4 @@ +/* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ @@ -12,6 +13,7 @@ // dqn_cgen.cpp // //////////////////////////////////////////////////////////////////////////////////////////////////// +*/ 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_CppArraySize, Dqn_CGenTableHeaderType_CppArraySizeField, + Dqn_CGenTableHeaderType_CppLabel, Dqn_CGenTableHeaderType_GenTypeInfo, }; @@ -57,6 +60,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST[] = Dqn_CGenTableHeaderType_Table, Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppValue, + Dqn_CGenTableHeaderType_CppLabel, Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenEnumCount, }; @@ -159,6 +163,7 @@ static bool Dqn_CGen_GatherTables_(Dqn_CGen *cgen, Dqn_ErrorSink *error) // NOTE: Validate table headers //////////////////////////////////////////////////////////// switch (table->type) { case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH; case Dqn_CGenTableType_Data: { } 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_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name); for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) { - MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node); has_error = true; 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_CppArraySize: result = DQN_STR8("cpp_array_size"); 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_GenEnumCount: result = DQN_STR8("gen_enum_count"); 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) { 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_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); 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++) { 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)); Dqn_OS_Exit(DQN_CAST(uint32_t)-1); } 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, ...) { if (!error) return; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr); + Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS(); MD_String8 kind_string = MD_StringFromMessageKind(kind); 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_start(args, fmt); - Dqn_Str8Builder_AppendFV(&builder, fmt, args); + Dqn_Str8Builder_AddFV(&builder, fmt, 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_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error) { bool result = true; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + builder.arena = tmem.arena; for (Dqn_usize index = 0; index < header_count; 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)); if (!slot) { 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) { - 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, table->headers_node, error, @@ -482,6 +494,26 @@ DQN_API bool Dqn_CGen_LookupNextTableInCodeGenTable(Dqn_CGen *cgen, Dqn_CGenTabl 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) { 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) break; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 prefix = Dqn_Str8_InitF(scratch.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment)); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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); Dqn_CppPrint(cpp, "%.*s", DQN_STR_FMT(prefix)); 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_ASSERT(Dqn_Str8_HasData(emit_prefix)); - if (emit & Dqn_CGenEmit_Prototypes) { // NOTE: Generate type info enums ////////////////////////////////////////////////////////// - Dqn_CppEnumBlock(cpp, "%.*s_Type", DQN_STR_FMT(emit_prefix)) { - Dqn_CppLine(cpp, "%.*s_Type_Nil,", DQN_STR_FMT(emit_prefix)); + Dqn_CppEnumBlock(cpp, "%.*sType", 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_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 ////////////////////////////////////////////////////////////// for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { switch (table->type) { - case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; - case Dqn_CGenTableType_Data: continue; + case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_CodeGenBuiltinTypes: continue; + case Dqn_CGenTableType_Data: continue; case Dqn_CGenTableType_CodeGenStruct: { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { // 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++) { 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_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); - 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) 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 = {}; 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, "%.*s%*s%.*s%.*s;", - DQN_STR_FMT(cpp_type.column.string), + DQN_STR_FMT(emit_cpp_type), name_to_type_padding, "", DQN_STR_FMT(cpp_name.column.string), DQN_STR_FMT(array_size)); } } + Dqn_CppNewLine(cpp); + Dqn_CppNewLine(cpp); } } break; case Dqn_CGenTableType_CodeGenEnum: { 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; for (Dqn_usize row_index = 0; row_index < it.table->row_count; 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); if (cpp_value.column.string.size) { 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(cpp_name.column.string), DQN_STR_FMT(cpp_value.column.string)); } else { 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(cpp_name.column.string), 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]; if (gen_enum_count_column.string.size) 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(gen_enum_count_column.string), enum_count); } + Dqn_CppNewLine(cpp); } } 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 ////////////////////////////////////////////////// for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { switch (table->type) { - case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; - case Dqn_CGenTableType_Data: continue; - case Dqn_CGenTableType_CodeGenEnum: continue; + case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Data: continue; + case Dqn_CGenTableType_CodeGenBuiltinTypes: continue; + case Dqn_CGenTableType_CodeGenEnum: continue; case Dqn_CGenTableType_CodeGenStruct: { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { 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++) { 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); if (cpp_name.column.string.size <= 0) continue; 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; } @@ -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);) { 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, "%.*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);) { 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; 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, "bool operator!=(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), 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)); + 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; 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) { + + // 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(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++) { 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); @@ -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_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_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"); 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_Scratch scratch = Dqn_Scratch_Get(nullptr); Dqn_Str8 cpp_array_size_field_str8 = DQN_STR8("NULL"); 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) index_the_field_references = sub_row_index; } - cpp_array_size_field_str8 = Dqn_Str8_InitF(scratch.arena, - "&g_%.*s_type_fields[%zu]", - DQN_STR_FMT(struct_name.string), - index_the_field_references); + cpp_array_size_field_str8 = + Dqn_Str8_InitF_TLS("&g_%.*s%.*s_type_fields[%zu]", + DQN_STR_FMT(emit_prefix), + 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_info = orig_cpp_type_info; - 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_name = cpp_type_list.data[row_index]; + Dqn_Str8 orig_cpp_type = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string); - 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_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_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_CppLine(cpp, - "{%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},", - row_index, - DQN_STR_FMT(cpp_name.column.string), - cpp_name_padding, "", + Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(orig_cpp_type)); + Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type.size - cpp_type_name.size); - // NOTE: offsetof(a, b) - DQN_STR_FMT(struct_or_enum_name), - DQN_STR_FMT(cpp_name.column.string), - cpp_name_padding, "", + Dqn_Str8 cpp_label_str8 = cpp_name.column.string; + Dqn_usize cpp_label_str8_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: sizeof(a->b) - DQN_STR_FMT(struct_or_enum_name), - DQN_STR_FMT(cpp_name.column.string), - cpp_name_padding, "", + Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena); - // NOTE: alignof(a->b) - DQN_STR_FMT(cpp_type.column.string), - cpp_type_padding, "", + // NOTE: row + Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index); - // NOTE: Type string - DQN_STR_FMT(cpp_type.column.string), - cpp_type_padding, "", + // NOTE: name + Dqn_Str8Builder_AddF(&builder, + "DQN_STR8(\"%.*s\"),%*s", + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, ""); - // NOTE: Type as enum - DQN_STR_FMT(cpp_type_enum), - cpp_type_enum_padding, "", + // NOTE: label + Dqn_Str8Builder_AddF(&builder, + "DQN_STR8(\"%.*s\"),%*s", + DQN_STR_FMT(cpp_label_str8), + cpp_label_str8_padding, ""); - 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)); + // NOTE: value + Dqn_Str8Builder_AddF(&builder, + "/*value*/ 0, "); + + + // 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 { 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_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_label = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppLabel].string, row); if (cpp_name.column.string.size <= 0) 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_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index); - Dqn_CppLine(cpp, - "{%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, + Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF_TLS("%zu", row_index); + Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name)); - // NOTE: Name string - DQN_STR_FMT(cpp_name.column.string), - cpp_name_padding, "", + Dqn_Str8 cpp_label_str8 = cpp_name.column.string; + Dqn_usize cpp_label_str8_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_STR_FMT(cpp_value_str8), + Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS(); + // NOTE: row + Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index); - // NOTE: sizeof(a) - DQN_STR_FMT(struct_or_enum_name), + // NOTE: name + Dqn_Str8Builder_AddF(&builder, + "DQN_STR8(\"%.*s\"),%*s", + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, ""); - // NOTE: alignof(a) - DQN_STR_FMT(struct_or_enum_name), + // NOTE: label + Dqn_Str8Builder_AddF(&builder, + "DQN_STR8(\"%.*s\"),%*s", + DQN_STR_FMT(cpp_label_str8), + cpp_label_str8_padding, ""); - // NOTE: ..._Type_... - DQN_STR_FMT(emit_prefix), - DQN_STR_FMT(struct_or_enum_name)); + // NOTE: value + Dqn_Str8Builder_AddF(&builder, "/*value*/ %.*s, ", DQN_STR_FMT(cpp_value_str8)); + + // 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; for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { 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_usize longest_type_name = 0; for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.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_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 = {}; char const *type_info_kind_padding = ""; 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); 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"); int fields_padding = 1; if (table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) { 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_STR8(\"%.*s\"),%*s%.*s, %ssizeof(%.*s),%*s/*fields*/ %.*s,%*s/*count*/ %.*s},", - DQN_STR_FMT(type_name), - name_padding, "", - DQN_STR_FMT(type_info_kind), - type_info_kind_padding, - DQN_STR_FMT(type_name), - name_padding, "", - DQN_STR_FMT(fields), - fields_padding, "", - DQN_STR_FMT(fields_count)); + Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS(); + + // NOTE: name + Dqn_Str8Builder_AddF(&builder, + "{DQN_STR8(\"%.*s\"),%*s", + DQN_STR_FMT(type_name), + name_padding, + ""); + + // NOTE: Dqn_TypeKind_{Nil|Basic|Enum|Struct} + 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);) { 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_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s_type_fields); index++", 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_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_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_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_CppNewLine(cpp); } } @@ -895,7 +1144,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C continue; 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++) { 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); @@ -955,11 +1204,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C } 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, "return result;"); } + Dqn_CppNewLine(cpp); } } } diff --git a/dqn_cgen.h b/dqn_cgen.h index ecb7a0d..6acab6f 100644 --- a/dqn_cgen.h +++ b/dqn_cgen.h @@ -1,6 +1,7 @@ #if !defined(DQN_CGEN_H) #define DQN_CGEN_H +/* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ @@ -15,6 +16,7 @@ // dqn_cgen.h -- C/C++ code generation from table data in Metadesk files // //////////////////////////////////////////////////////////////////////////////////////////////////// +*/ // NOTE: [$CGEN] Dqn_CGen ////////////////////////////////////////////////////////////////////////// #if !defined(MD_H) @@ -70,6 +72,7 @@ enum Dqn_CGenTableHeaderType Dqn_CGenTableHeaderType_CppOpEquals, Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySizeField, + Dqn_CGenTableHeaderType_CppLabel, Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenEnumCount, 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_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_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 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); diff --git a/dqn_containers.cpp b/dqn_containers.cpp index 85623da..d637e9d 100644 --- a/dqn_containers.cpp +++ b/dqn_containers.cpp @@ -56,14 +56,52 @@ DQN_API Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice< return result; } +DQN_API Dqn_Str16 Dqn_Slice_Str16Render(Dqn_Arena *arena, Dqn_Slice 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 array) +{ + Dqn_Str16 result = Dqn_Slice_Str16Render(arena, array, DQN_STR16(L" ")); + return result; +} + #if !defined(DQN_NO_DSMAP) // NOTE: [$DMAP] Dqn_DSMap ///////////////////////////////////////////////////////////////////////// DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64) { Dqn_DSMapKey result = {}; result.type = Dqn_DSMapKeyType_U64NoHash; - result.payload.u64 = u64; - result.hash = DQN_CAST(uint32_t)u64; + result.u64 = u64; + result.hash = DQN_CAST(uint32_t) u64; return result; } @@ -72,11 +110,15 @@ DQN_API bool Dqn_DSMap_KeyEquals(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs) bool result = false; if (lhs.type == rhs.type && lhs.hash == rhs.hash) { switch (lhs.type) { - case Dqn_DSMapKeyType_Invalid: 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_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_Invalid: result = true; break; + case Dqn_DSMapKeyType_U64NoHash: result = true; break; + case Dqn_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; 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; diff --git a/dqn_containers.h b/dqn_containers.h index 65ef3d1..9043beb 100644 --- a/dqn_containers.h +++ b/dqn_containers.h @@ -110,23 +110,22 @@ template using Dqn_FArray64 = Dqn_FArray; // NOTE: [$DMAP] Dqn_DSMap ///////////////////////////////////////////////////////////////////////// enum Dqn_DSMapKeyType { + // Key | Key Hash | Map Index Dqn_DSMapKeyType_Invalid, - Dqn_DSMapKeyType_U64, // Use a U64 key that is `hash(u64) % size` to map into the table - Dqn_DSMapKeyType_U64NoHash, // Use a U64 key that is `u64 % size` to map into the table - Dqn_DSMapKeyType_Buffer, // Use a buffer key that is `hash(buffer) % size` to map into the table + Dqn_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size + Dqn_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size + 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 { Dqn_DSMapKeyType type; - uint32_t hash; - union Payload { - struct Buffer { - void const *data; - uint32_t size; - } buffer; - uint64_t u64; - } payload; + uint32_t hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches + void const *buffer_data; + uint32_t buffer_size; + uint64_t u64; + bool no_copy_buffer; }; template @@ -136,6 +135,13 @@ struct Dqn_DSMapSlot 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); template struct Dqn_DSMap { @@ -147,6 +153,7 @@ template struct Dqn_DSMap 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 uint32_t hash_seed; // Seed for the hashing function, when 0, DQN_DS_MAP_DEFAULT_HASH_SEED is used + Dqn_DSMapFlags flags; }; template struct Dqn_DSMapResult @@ -179,7 +186,6 @@ template struct Dqn_ListIterator template struct Dqn_List { - Dqn_Arena *arena; 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_ListChunk *head; @@ -254,6 +260,7 @@ template void Dqn_SArra #endif // !defined(DQN_NO_SARRAY) #if !defined(DQN_NO_FARRAY) template Dqn_FArray Dqn_FArray_Init (T const *array, Dqn_usize count); +#define Dqn_FArray_HasData(array) ((array).data && (array).size) template Dqn_FArray Dqn_FArray_InitSlice (Dqn_Slice slice); template Dqn_FArray Dqn_FArray_InitCArray (T const (&items)[K]); template bool Dqn_FArray_IsValid (Dqn_FArray const *array); @@ -289,9 +296,11 @@ template Dqn_Slice Dqn_Slice template Dqn_Slice Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice array, Dqn_Str8 separator); Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice array); + Dqn_Str16 Dqn_Slice_Str16Render (Dqn_Arena *arena, Dqn_Slice array, Dqn_Str16 separator); + Dqn_Str16 Dqn_Slice_Str16RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice array); #endif // !defined(DQN_NO_SLICE) #if !defined(DQN_NO_DSMAP) -template Dqn_DSMap Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size); +template Dqn_DSMap Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags); template void Dqn_DSMap_Deinit (Dqn_DSMap *map, Dqn_ZeroMem zero_mem); template bool Dqn_DSMap_IsValid (Dqn_DSMap const *map); template uint32_t Dqn_DSMap_Hash (Dqn_DSMap const *map, Dqn_DSMapKey key); @@ -305,27 +314,28 @@ template Dqn_DSMapResult Dqn_DSMap template Dqn_DSMapResult Dqn_DSMap_FindKeyStr8 (Dqn_DSMap const *map, Dqn_Str8 key); template Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8 (Dqn_DSMap *map, Dqn_Str8 key); template Dqn_DSMapResult Dqn_DSMap_SetKeyStr8 (Dqn_DSMap *map, Dqn_Str8 key, T const &value); -template Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8Copy (Dqn_DSMap *map, Dqn_Arena *arena, Dqn_Str8 key); -template Dqn_DSMapResult Dqn_DSMap_SetKeyStr8Copy (Dqn_DSMap *map, Dqn_Arena *arena, Dqn_Str8 key, T const &value); template bool Dqn_DSMap_Resize (Dqn_DSMap *map, uint32_t size); template bool Dqn_DSMap_Erase (Dqn_DSMap *map, Dqn_DSMapKey key); template Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap const *map, void const *data, uint32_t size); +template Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash (Dqn_DSMap const *map, void const *data, uint32_t size); template Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap const *map, uint64_t u64); template Dqn_DSMapKey Dqn_DSMap_KeyStr8 (Dqn_DSMap const *map, Dqn_Str8 string); -template Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy (Dqn_DSMap 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) 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 operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); #endif // !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_LIST) -template Dqn_List Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size); +template Dqn_List Dqn_List_Init (Dqn_usize chunk_size); template Dqn_List Dqn_List_InitCArray (Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]); template T * Dqn_List_At (Dqn_List *list, Dqn_usize index, Dqn_ListChunk *at_chunk); template bool Dqn_List_Iterate (Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index); -template T * Dqn_List_Make (Dqn_List *list, Dqn_usize count); -template T * Dqn_List_Add (Dqn_List *list, T const &value); -template void Dqn_List_AddList (Dqn_List *list, Dqn_List other); +template T * Dqn_List_MakeArena (Dqn_List *list, Dqn_Arena *arena, Dqn_usize count); +template T * Dqn_List_MakePool (Dqn_List *list, Dqn_ChunkPool *pool, Dqn_usize count); +template T * Dqn_List_AddArena (Dqn_List *list, Dqn_Arena *arena, T const &value); +template T * Dqn_List_AddPool (Dqn_List *list, Dqn_ChunkPool *pool, T const &value); +template void Dqn_List_AddListArena (Dqn_List *list, Dqn_Arena *arena, Dqn_List other); +template void Dqn_List_AddListArena (Dqn_List *list, Dqn_ChunkPool *pool, Dqn_List other); template Dqn_Slice Dqn_List_ToSliceCopy (Dqn_List const *list, Dqn_Arena* arena); #endif // !defined(DQN_NO_LIST) @@ -932,19 +942,22 @@ template Dqn_Slice 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_SENTINEL_SLOT = 0; -template Dqn_DSMap Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size) +template Dqn_DSMap Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags) { Dqn_DSMap result = {}; if (!DQN_CHECKF(Dqn_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size)) return result; - if (!arena) + if (!DQN_CHECKF(size > 0, "Non-zero size must be given")) + return result; + if (!DQN_CHECK(arena)) return result; result.arena = arena; - result.hash_to_slot = Dqn_Arena_NewArray(result.arena, uint32_t, size, Dqn_ZeroMem_No); - result.slots = Dqn_Arena_NewArray(result.arena, Dqn_DSMapSlot, 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, size, Dqn_ZeroMem_Yes); result.occupied = 1; // For sentinel result.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?"); return result; } @@ -980,7 +993,12 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap const *map, Dqn_DSMapKey key) return result; 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; } @@ -998,17 +1016,18 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap const *map, Dqn_DSMapKey key) uint32_t len = 0; uint32_t h = seed; switch (key.type) { + case Dqn_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ case Dqn_DSMapKeyType_U64NoHash: DQN_INVALID_CODE_PATH; /*FALLTHRU*/ case Dqn_DSMapKeyType_Invalid: break; case Dqn_DSMapKeyType_Buffer: - key_ptr = DQN_CAST(char const *)key.payload.buffer.data; - len = key.payload.buffer.size; + key_ptr = DQN_CAST(char const *)key.buffer_data; + len = key.buffer_size; break; case Dqn_DSMapKeyType_U64: - key_ptr = DQN_CAST(char const *)&key.payload.u64; - len = sizeof(key.payload.u64); + key_ptr = DQN_CAST(char const *)&key.u64; + len = sizeof(key.u64); break; } @@ -1048,6 +1067,7 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap const *map, Dqn_DSMapKey key) template uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap const *map, Dqn_DSMapKey key) { + DQN_ASSERT(key.type != Dqn_DSMapKeyType_Invalid); uint32_t result = DQN_DS_MAP_SENTINEL_SLOT; if (!Dqn_DSMap_IsValid(map)) return result; @@ -1101,6 +1121,11 @@ Dqn_DSMapResult Dqn_DSMap_Make(Dqn_DSMap *map, Dqn_DSMapKey key) } else { result.slot = map->slots + map->hash_to_slot[index]; 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 { result.slot = map->slots + map->hash_to_slot[index]; @@ -1108,6 +1133,7 @@ Dqn_DSMapResult Dqn_DSMap_Make(Dqn_DSMap *map, Dqn_DSMapKey key) } result.value = &result.slot->value; + DQN_ASSERT(result.slot->key.type != Dqn_DSMapKeyType_Invalid); return result; } @@ -1171,30 +1197,6 @@ Dqn_DSMapResult Dqn_DSMap_SetKeyStr8(Dqn_DSMap *map, Dqn_Str8 key, T const return result; } -template -Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8Copy(Dqn_DSMap *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 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 -Dqn_DSMapResult Dqn_DSMap_SetKeyStr8Copy(Dqn_DSMap *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 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 bool Dqn_DSMap_Resize(Dqn_DSMap *map, uint32_t size) { @@ -1205,20 +1207,21 @@ bool Dqn_DSMap_Resize(Dqn_DSMap *map, uint32_t size) Dqn_Arena new_arena = {}; new_arena.flags = prev_arena->flags; - Dqn_DSMap new_map = Dqn_DSMap_Init(&new_arena, size); + Dqn_DSMap new_map = Dqn_DSMap_Init(&new_arena, size, map->flags); if (!Dqn_DSMap_IsValid(&new_map)) return false; new_map.initial_size = map->initial_size; for (uint32_t old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) { Dqn_DSMapSlot *old_slot = map->slots + old_index; - if (old_slot->key.type != Dqn_DSMapKeyType_Invalid) { - Dqn_DSMap_Set(&new_map, old_slot->key, old_slot->value); - } + Dqn_DSMapKey old_key = old_slot->key; + 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); - Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No); + if ((map->flags & Dqn_DSMapFlags_DontFreeArenaOnResize) == 0) + Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No); *map = new_map; // Update the map inplace 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 @@ -1269,14 +1272,17 @@ bool Dqn_DSMap_Erase(Dqn_DSMap *map, Dqn_DSMapKey key) // We will now fill in the vacant spot that we erased using the last // element in the slot list. if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) { - // NOTE: Copy in last slot to the erase slot - Dqn_DSMapSlot *last_slot = map->slots + map->occupied - 1; - map->slots[slot_index] = *last_slot; + uint32_t last_index = map->occupied - 1; + if (last_index != slot_index) { + // NOTE: Copy in last slot to the erase slot + Dqn_DSMapSlot *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 - uint32_t hash_to_slot_index = Dqn_DSMap_HashToSlotIndex(map, last_slot->key); - map->hash_to_slot[hash_to_slot_index] = slot_index; - *last_slot = {}; // TODO: Optional? + // 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); + map->hash_to_slot[hash_to_slot_index] = slot_index; + *last_slot = {}; // TODO: Optional? + } } } @@ -1289,53 +1295,52 @@ bool Dqn_DSMap_Erase(Dqn_DSMap *map, Dqn_DSMapKey key) } template -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap const *map, void const *data, uint32_t 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 -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap const *map, uint64_t u64) +Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap const *map, void const *data, Dqn_usize size) { + DQN_ASSERT(size > 0 && size <= UINT32_MAX); Dqn_DSMapKey result = {}; - result.type = Dqn_DSMapKeyType_U64; - result.payload.u64 = u64; + result.type = Dqn_DSMapKeyType_Buffer; + result.buffer_data = data; + result.buffer_size = DQN_CAST(uint32_t) size; result.hash = Dqn_DSMap_Hash(map, result); return result; } template -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap const *map, Dqn_Str8 string) +Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash(Dqn_DSMap const *map, void const *data, uint32_t size) { - DQN_ASSERT(string.size > 0 && string.size <= UINT32_MAX); - Dqn_DSMapKey result = {}; - result.type = Dqn_DSMapKeyType_Buffer; - result.payload.buffer.data = string.data; - result.payload.buffer.size = DQN_CAST(uint32_t)string.size; - result.hash = Dqn_DSMap_Hash(map, result); + Dqn_DSMapKey result = {}; + result.type = Dqn_DSMapKeyType_BufferAsU64NoHash; + result.buffer_data = data; + result.buffer_size = DQN_CAST(uint32_t) size; + DQN_ASSERT(size >= sizeof(result.hash)); + DQN_MEMCPY(&result.hash, data, sizeof(result.hash)); return result; } template -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy(Dqn_DSMap const *map, Dqn_Arena *arena, Dqn_Str8 string) +Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap const *map, uint64_t u64) { - Dqn_Str8 copy = Dqn_Str8_Copy(arena, string); - Dqn_DSMapKey result = Dqn_DSMap_KeyStr8(map, copy); + Dqn_DSMapKey result = {}; + result.type = Dqn_DSMapKeyType_U64; + result.u64 = u64; + result.hash = Dqn_DSMap_Hash(map, result); + return result; +} + +template +Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap const *map, Dqn_Str8 string) +{ + Dqn_DSMapKey result = Dqn_DSMap_KeyBuffer(map, string.data, string.size); return result; } #endif // !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_LIST) // NOTE: [$LIST] Dqn_List ////////////////////////////////////////////////////////////////////////// -template Dqn_List Dqn_List_Init(Dqn_Arena *arena, Dqn_usize chunk_size) +template Dqn_List Dqn_List_Init(Dqn_usize chunk_size) { Dqn_List result = {}; - result.arena = arena; result.chunk_size = chunk_size; return result; } @@ -1356,31 +1361,68 @@ template Dqn_List Dqn_List_InitSliceCopy(Dqn_Arena *arena, Dqn_u return result; } -template DQN_API T *Dqn_List_Make(Dqn_List *list, Dqn_usize count) +template DQN_API bool Dqn_List_AttachTail_(Dqn_List *list, Dqn_ListChunk *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 DQN_API Dqn_ListChunk *Dqn_List_AllocArena_(Dqn_List *list, Dqn_Arena *arena, Dqn_usize count) +{ + auto *result = Dqn_Arena_New(arena, Dqn_ListChunk, 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 DQN_API Dqn_ListChunk *Dqn_List_AllocPool_(Dqn_List *list, Dqn_ChunkPool *pool, Dqn_usize count) +{ + auto *result = Dqn_ChunkPool_New(pool, Dqn_ListChunk); + 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 DQN_API T *Dqn_List_MakeArena(Dqn_List *list, Dqn_Arena *arena, Dqn_usize count) { if (list->chunk_size == 0) list->chunk_size = 128; + if (!list->tail || (list->tail->count + count) > list->tail->size) { - auto *tail = Dqn_Arena_New(list->arena, Dqn_ListChunk, Dqn_ZeroMem_Yes); - 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) + if (!Dqn_List_AllocArena_(list, arena, count)) 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; @@ -1389,9 +1431,32 @@ template DQN_API T *Dqn_List_Make(Dqn_List *list, Dqn_usize coun return result; } -template DQN_API T *Dqn_List_Add(Dqn_List *list, T const &value) +template DQN_API T *Dqn_List_MakePool(Dqn_List *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 DQN_API T *Dqn_List_AddArena(Dqn_List *list, Dqn_Arena *arena, T const &value) +{ + T *result = Dqn_List_MakeArena(list, arena, 1); + *result = value; + return result; +} + +template DQN_API T *Dqn_List_AddPool(Dqn_List *list, Dqn_ChunkPool *pool, T const &value) +{ + T *result = Dqn_List_MakePool(list, pool, 1); *result = value; return result; } @@ -1407,14 +1472,22 @@ template DQN_API bool Dqn_List_AddCArray(Dqn_List *lis return true; } -template DQN_API void Dqn_List_AddList(Dqn_List *list, Dqn_List other) +template DQN_API void Dqn_List_AddListArena(Dqn_List *list, Dqn_Arena *arena, Dqn_List other) { if (!list || list->chunk_size <= 0) return; - // TODO(doyle): Copy chunk by chunk - for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); ) - Dqn_List_Add(list, *it.data); + for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); ) + Dqn_List_AddArena(list, arena, *it.data); +} + +template DQN_API void Dqn_List_AddListPool(Dqn_List *list, Dqn_ChunkPool *pool, Dqn_List other) +{ + if (!list || list->chunk_size <= 0) + return; + // TODO(doyle): Copy chunk by chunk + for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); ) + Dqn_List_AddPool(list, pool, *it.data); } template DQN_API bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index) @@ -1497,8 +1570,11 @@ template Dqn_Slice Dqn_List_ToSliceCopy(Dqn_List const *list, Dqn_Slice result = Dqn_Slice_Alloc(arena, list->count, Dqn_ZeroMem_No); if (result.size) { Dqn_usize slice_index = 0; + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6011) // Dereferencing NULL pointer 'x' for (Dqn_ListIterator it = {}; Dqn_List_Iterate(DQN_CAST(Dqn_List *)list, &it, 0);) result.data[slice_index++] = *it.data; + DQN_MSVC_WARNING_POP DQN_ASSERT(slice_index == result.size); } return result; diff --git a/dqn_debug.cpp b/dqn_debug.cpp index 32ccd99..39bb3dc 100644 --- a/dqn_debug.cpp +++ b/dqn_debug.cpp @@ -67,8 +67,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t g_dqn_library->win32_sym_initialised = true; SymSetOptions(SYMOPT_LOAD_LINES); if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(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)); } } @@ -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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(scratch.arena, limit); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(tmem.arena, limit); Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip); return result; } @@ -128,7 +128,7 @@ static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const for (Dqn_usize index = skip; index < walk->size; index++) { raw_frame.base_addr = walk->base_addr[index]; 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) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); + Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena); Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip); result = Dqn_Str8Builder_Build(&builder, arena); return result; @@ -181,9 +180,8 @@ DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const if (!walk) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena); Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip); result = Dqn_Str8Builder_BuildCRT(&builder); return result; @@ -196,8 +194,8 @@ DQN_API Dqn_Slice Dqn_StackTrace_GetFrames(Dqn_Arena *arena if (!arena) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); + Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(tmem.arena, limit); if (!walk.size) 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Slice stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Slice stack_trace = Dqn_StackTrace_GetFrames(tmem.arena, limit); 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)); } diff --git a/dqn_docs.cpp b/dqn_docs.cpp index 02d6d17..f318e1a 100644 --- a/dqn_docs.cpp +++ b/dqn_docs.cpp @@ -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 ///////////////////////////////////////////////////////////////////////////// // // 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 // the map takes ownership of the arena). Dqn_Arena arena = {}; - Dqn_DSMap map = Dqn_DSMap_Init(&arena, /*size*/ 1024); // Size must be PoT! - DQN_ASSERT(Dqn_DSMap_IsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure) + Dqn_DSMap map = Dqn_DSMap_Init(&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) // NOTE: Dqn_DSMap_KeyCStringLit /////////////////////////////////////////////////////////// // NOTE: Dqn_DSMap_KeyU64 /////////////////////////////////////////////////////////// @@ -204,7 +212,7 @@ void Dqn_Docs_Demo() int *it_value = &it->value; 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 /////////////////////////////////////////////////////////////////// @@ -327,6 +335,15 @@ void Dqn_Docs_Demo() // The lifetime of the slice is bound to the lifetime of the FStr8 and 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 ///////////////////////////////////////////////////////////////// // // Convert the internal JSON buffer in the builder into a string. @@ -371,8 +388,8 @@ void Dqn_Docs_Demo() // NOTE: Dqn_List_Iterate ////////////////////////////////////////////////////////////////////// { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_List list = Dqn_List_Init(scratch.arena, /*chunk_size*/ 128); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_List list = Dqn_List_Init(/*chunk_size*/ 128); for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&list, &it, 0);) { int *item = it.data; (void)item; @@ -450,7 +467,7 @@ void Dqn_Docs_Demo() // If 'tmp_path' is written to successfuly, the file will be copied over into // 'path'. 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_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"), error); Dqn_ErrorSink_EndAndLogErrorF(error, ""); @@ -550,12 +567,11 @@ void Dqn_Docs_Demo() // that can be converted into a duration. // // 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 - // buffer whereby the front buffer contains the previous frames profiling - // metrics and the back buffer will be populated with the new frame's profiling - // metrics. + // After an application's typical update/frame cycle you can swap the + // profiler's buffer whereby the front buffer contains the previous frames + // profiling metrics and the back buffer will be populated with the new + // 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 }; 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 // regarding the most profiling metrics recorded. - Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Front); - for (size_t index = 0; index < Zone_Count; index++) { - Dqn_ProfilerAnchor *anchor = anchors + index; - - // Print the result like so - if (0) { - printf("%.*s[%u] %" PRIu64 " cycles (%.1fms)\n", - DQN_STR_FMT(anchor->name), - anchor->hit_count, - anchor->tsc_inclusive, - anchor->tsc_inclusive * tsc_per_seconds * 1000.f); + // NOTE: Dqn_Profiler_ReadBuffer /////////////////////////////////////////////////////////// + // + // Retrieve the buffer of anchors of which there are + // `DQN_PROFILER_ANCHOR_BUFFER_SIZE` anchors from the most recent run + // of the profiler after you have called `SwapAnchorBuffer` to trigger + // the double buffer + Dqn_ProfilerAnchor *read_anchors = Dqn_Profiler_ReadBuffer(); + for (Dqn_usize index = 0; index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; index++) { + Dqn_ProfilerAnchor *anchor = read_anchors + index; + if (Dqn_Str8_HasData(anchor->name)) { + // ... } } + + // 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_SwapAnchorBuffer(); // Should occur after all profiling zones are ended! - *g_dqn_library->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 // into the address space. { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); // NOTE: Dqn_StackTrace_Walk /////////////////////////////////////////////////////////////// // @@ -680,7 +708,7 @@ void Dqn_Docs_Demo() // functions on the call-stack at the current instruction pointer. The // addresses are stored in order from the current executing function // 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 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 // 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 if (0) @@ -706,7 +734,7 @@ void Dqn_Docs_Demo() // Helper function to create a stack trace and automatically convert the // raw frames into human readable frames. This function effectively // calls 'Walk' followed by 'RawFrameToFrame'. - Dqn_Slice frames = Dqn_StackTrace_GetFrames(scratch.arena, /*depth limit*/ 128); + Dqn_Slice frames = Dqn_StackTrace_GetFrames(tmem.arena, /*depth limit*/ 128); (void)frames; } @@ -719,8 +747,8 @@ void Dqn_Docs_Demo() // The returned string's 'size' member variable does *not* include this // additional null-terminating byte. { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, /*size*/ 1, Dqn_ZeroMem_Yes); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, /*size*/ 1, Dqn_ZeroMem_Yes); DQN_ASSERT(string.size == 1); 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 // 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"), /*find*/ DQN_STR8("Foo"), /*replace*/ DQN_STR8("Moo"), /*start_index*/ 1, - /*arena*/ scratch.arena, + /*arena*/ tmem.arena, /*eq_case*/ Dqn_Str8EqCase_Sensitive); 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 // of the string. { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 string = Dqn_Str8_Segment(scratch.arena, /*string*/ DQN_STR8("123456789"), /*segment_size*/ 3, /*segment_char*/ ','); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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")); } @@ -826,9 +854,9 @@ void Dqn_Docs_Demo() { // Splits the string at each delimiter into substrings occuring prior and // after until the next delimiter. - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); { - Dqn_Slice splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena, + Dqn_Slice splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena, /*string*/ DQN_STR8("192.168.8.1"), /*delimiter*/ DQN_STR8("."), /*mode*/ Dqn_Str8SplitIncludeEmptyStrings_No); @@ -839,7 +867,7 @@ void Dqn_Docs_Demo() // You can include empty strings that occur when splitting by setting // the split mode to include empty strings. { - Dqn_Slice splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena, + Dqn_Slice splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena, /*string*/ DQN_STR8("a--b"), /*delimiter*/ DQN_STR8("-"), /*mode*/ Dqn_Str8SplitIncludeEmptyStrings_Yes); @@ -953,7 +981,7 @@ void Dqn_Docs_Demo() // 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 // lifetime scoped to the lexical scope or for storing data permanently // using the arena paradigm. @@ -961,40 +989,40 @@ void Dqn_Docs_Demo() // TLS in this implementation is implemented using the `thread_local` C/C++ // 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 // 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 // passed into a function, this poses a problem sometimes known as // 'arena aliasing'. // // 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. // - // To avoid this we the 'Dqn_Scratch_Get(...)' API takes in a list of arenas - // to ensure that we provide a scratch arena that *won't* alias with the + // To avoid this we the 'Dqn_TLS_TMem(...)' API takes in a list of arenas + // 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 // 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 - // into it .. If we call scratch again, we need to pass in the arena + // Now imagine we call a function where we pass tmem_a.arena down + // into it .. If we call tmem again, we need to pass in the arena // to prevent aliasing. - Dqn_Scratch scratch_b = Dqn_Scratch_Get(scratch_a.arena); - DQN_ASSERT(scratch_a.arena != scratch_b.arena); + Dqn_TLSTMem tmem_b = Dqn_TLS_TMem(tmem_a.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 // 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 - // 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 // 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 ////////////////////////////////////////////////////////////////////////// { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 string = Dqn_U64ToAge(scratch.arena, DQN_HOURS_TO_S(2) + DQN_MINS_TO_S(30), Dqn_U64AgeUnit_All); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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" printf("%.*s", DQN_STR_FMT(string)); } @@ -1131,12 +1159,12 @@ void Dqn_Docs_Demo() if (0) { // Generate the error string for the last Win32 API called that return // an error value. - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_WinError get_last_error = Dqn_Win_LastError(scratch.arena); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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)); // 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)); } diff --git a/dqn_helpers.cpp b/dqn_helpers.cpp index cd37abc..73214b2 100644 --- a/dqn_helpers.cpp +++ b/dqn_helpers.cpp @@ -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; if (key.size) { - Dqn_Str8Builder_AppendF(&builder->string_builder, - "%.*s%*c\"%.*s\": %.*s", - prefix_size, - prefix, - spaces, - ' ', - DQN_STR_FMT(key), - DQN_STR_FMT(value)); + Dqn_Str8Builder_AddF(&builder->string_builder, + "%.*s%*c\"%.*s\": %.*s", + prefix_size, + prefix, + spaces, + ' ', + DQN_STR_FMT(key), + DQN_STR_FMT(value)); } else { - Dqn_Str8Builder_AppendF( - &builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value)); + Dqn_Str8Builder_AddF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value)); } 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_Scratch scratch = Dqn_Scratch_Get(builder->string_builder.arena); - Dqn_Str8 value = Dqn_Str8_InitFV(scratch.arena, value_fmt, args); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(builder->string_builder.arena); + Dqn_Str8 value = Dqn_Str8_InitFV(tmem.arena, value_fmt, args); Dqn_JSONBuilder_KeyValue(builder, key, value); } @@ -765,55 +764,55 @@ DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString(Dqn_U64ByteSizeType type) 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 = {}; if (!arena) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); Dqn_Str8Builder builder = {}; builder.arena = arena; uint64_t remainder = age_s; - if (type & Dqn_U64AgeUnit_Year) { - Dqn_usize unit = remainder / DQN_YEARS_TO_S(1); - remainder -= DQN_YEARS_TO_S(unit); - if (unit) - Dqn_Str8Builder_AppendF(&builder, "%s%I64uyr", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Year) { + Dqn_usize value = remainder / DQN_YEARS_TO_S(1); + remainder -= DQN_YEARS_TO_S(value); + if (value) + Dqn_Str8Builder_AddF(&builder, "%s%I64uyr", builder.string_size ? " " : "", value); } - if (type & Dqn_U64AgeUnit_Week) { - Dqn_usize unit = remainder / DQN_WEEKS_TO_S(1); - remainder -= DQN_WEEKS_TO_S(unit); - if (unit) - Dqn_Str8Builder_AppendF(&builder, "%s%I64uw", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Week) { + Dqn_usize value = remainder / DQN_WEEKS_TO_S(1); + remainder -= DQN_WEEKS_TO_S(value); + if (value) + Dqn_Str8Builder_AddF(&builder, "%s%I64uw", builder.string_size ? " " : "", value); } - if (type & Dqn_U64AgeUnit_Day) { - Dqn_usize unit = remainder / DQN_DAYS_TO_S(1); - remainder -= DQN_DAYS_TO_S(unit); - if (unit) - Dqn_Str8Builder_AppendF(&builder, "%s%I64ud", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Day) { + Dqn_usize value = remainder / DQN_DAYS_TO_S(1); + remainder -= DQN_DAYS_TO_S(value); + if (value) + Dqn_Str8Builder_AddF(&builder, "%s%I64ud", builder.string_size ? " " : "", value); } - if (type & Dqn_U64AgeUnit_Hr) { - Dqn_usize unit = remainder / DQN_HOURS_TO_S(1); - remainder -= DQN_HOURS_TO_S(unit); - if (unit) - Dqn_Str8Builder_AppendF(&builder, "%s%I64uh", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Hr) { + Dqn_usize value = remainder / DQN_HOURS_TO_S(1); + remainder -= DQN_HOURS_TO_S(value); + if (value) + Dqn_Str8Builder_AddF(&builder, "%s%I64uh", builder.string_size ? " " : "", value); } - if (type & Dqn_U64AgeUnit_Min) { - Dqn_usize unit = remainder / DQN_MINS_TO_S(1); - remainder -= DQN_MINS_TO_S(unit); - if (unit) - Dqn_Str8Builder_AppendF(&builder, "%s%I64um", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Min) { + Dqn_usize value = remainder / DQN_MINS_TO_S(1); + remainder -= DQN_MINS_TO_S(value); + if (value) + Dqn_Str8Builder_AddF(&builder, "%s%I64um", builder.string_size ? " " : "", value); } - if (type & Dqn_U64AgeUnit_Sec) { - Dqn_usize unit = remainder; - Dqn_Str8Builder_AppendF(&builder, "%s%I64us", builder.string_size ? " " : "", unit); + if (unit & Dqn_U64AgeUnit_Sec) { + Dqn_usize value = remainder; + Dqn_Str8Builder_AddF(&builder, "%s%I64us", builder.string_size ? " " : "", value); } 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_Str8 prefix = {}; - if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + if ((flags & Dqn_HexU64Str8Flags_0xPrefix)) 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_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_Str8 prefix = {}; - if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + if (flags & Dqn_HexU64Str8Flags_0xPrefix) prefix = DQN_STR8("0x"); Dqn_U64HexStr8 result = {}; DQN_MEMCPY(result.data, prefix.data, 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); result.size += DQN_CAST(uint8_t) size; 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) return result; - result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No); - result.data[result.size - 1] = 0; - bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size); + result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No); + result.data[result.size] = 0; + bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size); DQN_ASSERT(converted); 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 // the arena we're using to initialise the table. result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack; - result->alloc_table = Dqn_DSMap_Init(&result->alloc_table_arena, 4096); + result->alloc_table = Dqn_DSMap_Init(&result->alloc_table_arena, 4096, Dqn_DSMapFlags_Nil); #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); Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool); 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"); #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 - // of scratch memory may request scratch memory itself in leak tracing mode. - // This is supported as the scratch arenas defer allocation tracking until + // of tmem memory may request tmem memory itself in leak tracing mode. + // This is supported as the tmem arenas defer allocation tracking until // 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 ///////////////////////////////////////////////////// result->exe_dir = Dqn_OS_EXEDir(&result->arena); // NOTE: Print out init features /////////////////////////////////////////////////////////////// Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + builder.arena = tmem.arena; 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 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); #if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) if (DQN_ASAN_POISON) { - Dqn_Str8Builder_AppendF( + Dqn_Str8Builder_AddF( &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 #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 #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 #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 - Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stb_sprintf functions\n")); + Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Using stb_sprintf functions\n")); #endif // 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) { Dqn_CPUReport const *report = &result->cpu_report; Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1)); - Dqn_Str8Builder_AppendF(&builder, - " CPU '%.*s' from '%s' detected:\n", - DQN_STR_FMT(brand), - report->vendor); + Dqn_Str8Builder_AddF(&builder, + " CPU '%.*s' from '%s' detected:\n", + DQN_STR_FMT(brand), + report->vendor); Dqn_usize longest_feature_name = 0; 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_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index]; bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value); - Dqn_Str8Builder_AppendF(&builder, - " %.*s:%*s%s\n", - DQN_STR_FMT(feature_decl.label), - DQN_CAST(int)(longest_feature_name - feature_decl.label.size), - "", - has_feature ? "available" : "not available"); + Dqn_Str8Builder_AddF(&builder, + " %.*s:%*s%s\n", + DQN_STR_FMT(feature_decl.label), + DQN_CAST(int)(longest_feature_name - feature_decl.label.size), + "", + 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)) Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log)); 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) { - if (library) + if (library) { g_dqn_library = library; + Dqn_OS_ThreadSetTLS(&library->tls); + } } #if !defined(DQN_NO_PROFILER) @@ -1216,21 +1219,42 @@ DQN_API Dqn_Arena *Dqn_Library_AllocArenaF(Dqn_usize reserve, Dqn_usize commit, return result; } -#if !defined(DQN_NO_PROFILER) -// NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// -Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index) +DQN_API bool Dqn_Library_EraseArena(Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena) { - 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_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; Dqn_ProfilerZone result = {}; result.begin_tsc = Dqn_CPU_TSC(); @@ -1241,10 +1265,10 @@ Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_ 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; - 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; anchor->hit_count++; @@ -1256,7 +1280,7 @@ void Dqn_Profiler_EndZone(Dqn_ProfilerZone 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 anchor_buffer = @@ -1265,7 +1289,7 @@ Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer) return result; } -void Dqn_Profiler_SwapAnchorBuffer() +DQN_API void Dqn_Profiler_SwapAnchorBuffer() { g_dqn_library->profiler->active_anchor_buffer++; 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])); } -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++) { Dqn_ProfilerAnchor const *anchor = anchors + anchor_index; 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_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context; - uint32_t const pot_mask = DQN_ARRAY_UCOUNT(queue->jobs) - 1; + Dqn_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context; + 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"); for (;;) { @@ -1360,8 +1384,9 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread) queue->read_index += 1; Dqn_OS_MutexUnlock(&queue->mutex); - job.func(job.user_context); - Dqn_Arena_Deinit(job.arena); + job.elapsed_tsc -= Dqn_CPU_TSC(); + job.func(thread, job.user_context); + job.elapsed_tsc += Dqn_CPU_TSC(); if (job.add_to_completion_queue) { 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); } + // NOTE: Update finish counter Dqn_OS_MutexLock(&queue->mutex); 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, queue->threads_waiting_for_completion); queue->threads_waiting_for_completion = 0; diff --git a/dqn_helpers.h b/dqn_helpers.h index 40324cc..c9336ba 100644 --- a/dqn_helpers.h +++ b/dqn_helpers.h @@ -64,8 +64,7 @@ enum Dqn_BinarySearchType { // 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 - // it were in the array (in practice this turns out to be the first or - // [last index + 1] of the array). + // it were in the array Dqn_BinarySearchType_Match, // 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; }; +template +using Dqn_QSortLessThanProc = bool(T const &a, T const &b, void *user_context); + // NOTE: [$MISC] Misc ////////////////////////////////////////////////////////////////////////////// struct Dqn_U64Str8 { @@ -122,7 +124,8 @@ struct Dqn_U64ByteSize Dqn_f64 bytes; }; -enum Dqn_U64AgeUnit +typedef uint32_t Dqn_U64AgeUnit; +enum Dqn_U64AgeUnit_ { Dqn_U64AgeUnit_Sec = 1 << 0, Dqn_U64AgeUnit_Min = 1 << 1, @@ -142,8 +145,9 @@ struct Dqn_U64HexStr8 enum Dqn_U64HexStr8Flags { - Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string - Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex + Dqn_HexU64Str8Flags_Nil = 0, + 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) @@ -182,12 +186,12 @@ struct Dqn_ProfilerZoneScope ~Dqn_ProfilerZoneScope(); 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_ZoneScope(name) Dqn_Profiler_ZoneScopeWithIndex(name, __COUNTER__ + 1) +#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_ZoneScopeAtIndex(name, __COUNTER__ + 1) #endif #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_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) // NOTE: [$JOBQ] Dqn_JobQueue /////////////////////////////////////////////////////////////////////// -typedef void (Dqn_JobQueueFunc)(void *user_context); +typedef void (Dqn_JobQueueFunc)(Dqn_OSThread *thread, void *user_context); struct Dqn_Job { - bool add_to_completion_queue; - Dqn_Arena *arena; - Dqn_JobQueueFunc *func; - void *user_context; + Dqn_JobQueueFunc *func; // The function to invoke for the job + void *user_context; // Pointer user can set to use in their `job_func` + uint64_t elapsed_tsc; + 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) @@ -248,13 +253,14 @@ struct Dqn_Library { bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` Dqn_TicketMutex lib_mutex; - Dqn_TicketMutex thread_context_init_mutex; Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Arena arena; Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations Dqn_ArenaCatalog arena_catalog; bool slow_verification_checks; // Enable expensive library verification checks Dqn_CPUReport cpu_report; + Dqn_TLS tls; // Thread local storage state for the main thread. + // NOTE: Logging /////////////////////////////////////////////////////////////////////////////// Dqn_LogProc * log_callback; // Set this pointer to override the logging routine void * log_user_data; @@ -262,22 +268,27 @@ struct Dqn_Library 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 bool log_no_colour; // Disable colours in the logging output + // NOTE: Leak Tracing ////////////////////////////////////////////////////////////////////////// #if defined(DQN_LEAK_TRACKING) Dqn_DSMap alloc_table; Dqn_TicketMutex alloc_table_mutex; Dqn_Arena alloc_table_arena; #endif + // NOTE: Win32 ///////////////////////////////////////////////////////////////////////////////// #if defined(DQN_OS_WIN32) LARGE_INTEGER win32_qpc_frequency; Dqn_TicketMutex win32_bcrypt_rng_mutex; void * win32_bcrypt_rng_handle; #endif + bool win32_sym_initialised; + // NOTE: OS //////////////////////////////////////////////////////////////////////////////////// uint32_t os_page_size; uint32_t os_alloc_granularity; + // NOTE: Profiler ////////////////////////////////////////////////////////////////////////////// #if !defined(DQN_NO_PROFILER) Dqn_Profiler *profiler; @@ -354,6 +365,13 @@ template Dqn_BinarySearchResult Dqn_BinarySearch (T Dqn_BinarySearchType type = Dqn_BinarySearchType_Match, Dqn_BinarySearchLessThanProc less_than = Dqn_BinarySearch_DefaultLessThan); +// NOTE: [$QSOR] Dqn_QSort ///////////////////////////////////////////////////////////////////////// +template bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs); +template void Dqn_QSort (T *array, + Dqn_usize array_size, + void *user_context, + Dqn_QSortLessThanProc less_than = Dqn_QSort_DefaultLessThan); + // NOTE: [$BITS] Dqn_Bit /////////////////////////////////////////////////////////////////////////// DQN_API void Dqn_Bit_UnsetInplace (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_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_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 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 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); +#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_HexToBytesPtr (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_size); 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); +#define Dqn_HexToBytes_TLS(...) Dqn_HexToBytes(Dqn_TLS_TopArena(), __VA_ARGS__) // NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// -#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1) -DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index); +DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_ReadBuffer (); +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 Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer); 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_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 bool Dqn_Library_EraseArena (Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena); // NOTE: [$BSEA] Dqn_BinarySearch ////////////////////////////////////////////////////////////////// template @@ -482,7 +506,7 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array, Dqn_BinarySearchLessThanProc less_than) { Dqn_BinarySearchResult result = {}; - if (!array || array_size <= 0) + if (!array || array_size <= 0 || !less_than) return result; T const *end = array + array_size; @@ -519,3 +543,73 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array, return result; } +// NOTE: [$QSOR] Dqn_QSort ///////////////////////////////////////////////////////////////////////// +template +bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context) +{ + (void)user_context; + bool result = lhs < rhs; + return result; +} + +template +void Dqn_QSort(T *array, Dqn_usize array_size, void *user_context, Dqn_QSortLessThanProc 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); +} diff --git a/dqn_json.h b/dqn_json.h index be2823c..4e460f2 100644 --- a/dqn_json.h +++ b/dqn_json.h @@ -2,7 +2,7 @@ #include "dqn.h" #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 // NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// diff --git a/dqn_math.cpp b/dqn_math.cpp index 534ace4..6bda798 100644 --- a/dqn_math.cpp +++ b/dqn_math.cpp @@ -1212,10 +1212,10 @@ DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat) Dqn_FStr8<256> result = {}; for (int row = 0; row < 4; row++) { for (int it = 0; it < 4; it++) { - if (it == 0) Dqn_FStr8_Append(&result, DQN_STR8("|")); - Dqn_FStr8_AppendF(&result, "%.5f", mat.columns[it][row]); - if (it != 3) Dqn_FStr8_Append(&result, DQN_STR8(", ")); - else Dqn_FStr8_Append(&result, DQN_STR8("|\n")); + if (it == 0) Dqn_FStr8_Add(&result, DQN_STR8("|")); + Dqn_FStr8_AddF(&result, "%.5f", mat.columns[it][row]); + if (it != 3) Dqn_FStr8_Add(&result, DQN_STR8(", ")); + else Dqn_FStr8_Add(&result, DQN_STR8("|\n")); } } diff --git a/dqn_os.cpp b/dqn_os.cpp index 8a592ba..8ea1639 100644 --- a/dqn_os.cpp +++ b/dqn_os.cpp @@ -75,8 +75,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena) Dqn_Str8 result = {}; if (!arena) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); + Dqn_Str8 exe_path = Dqn_OS_EXEPath(tmem.arena); Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators)); 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) // 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) { 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; if (!file || !fmt) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args); result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error); 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 /////////////////////////////////////////////////////////////////////////// -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 = {}; 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); result = Dqn_Str8_Alloc(arena, path_info.size, Dqn_ZeroMem_No); if (!Dqn_Str8_HasData(result)) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, path_info.size, Dqn_U64ByteSizeType_Auto); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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_Arena_TempMemEnd(temp_mem); 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args); bool result = Dqn_OS_WriteAll(file_path, buffer, error); 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path)); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 tmp_path = Dqn_Str8_InitF(tmem.arena, "%.*s.tmp", DQN_STR_FMT(path)); if (!Dqn_OS_WriteAll(tmp_path, buffer, error)) return false; 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args); bool result = Dqn_OS_WriteAllSafe(path, buffer, error); return result; } @@ -375,7 +383,7 @@ DQN_API bool Dqn_OS_PathPop(Dqn_OSPath *fs_path) 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_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; } -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_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); - Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, path_separator); + Dqn_Str8 result = Dqn_OS_PathTo(arena, path, path_separator); 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; } -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_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); - Dqn_Str8 result = Dqn_OS_PathConvert(arena, path); + Dqn_Str8 result = Dqn_OS_Path(arena, path); return result; } @@ -480,6 +488,23 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice cmd_line, Dqn_St 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 //////////////////////////////////////////////////////////////////////// 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) { // TODO(doyle): Revise the memory allocation and its lifetime - Dqn_OSHttpResponse result = {}; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - result.scratch_arena = scratch.arena; + Dqn_OSHttpResponse result = {}; + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); + result.tmem_arena = tmem.arena; Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); Dqn_OS_HttpRequestWait(&result); diff --git a/dqn_os.h b/dqn_os.h index 346e02f..0f9fbec 100644 --- a/dqn_os.h +++ b/dqn_os.h @@ -122,6 +122,13 @@ struct Dqn_OSPathInfo uint64_t size; }; +struct Dqn_OS_DirIterator +{ + void *handle; + Dqn_Str8 file_name; + char buffer[512]; +}; + // NOTE: R/W Stream API //////////////////////////////////////////////////////////////////////////// struct Dqn_OSFile { @@ -231,6 +238,7 @@ typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*); struct Dqn_OSThread { + Dqn_TLS tls; void *handle; uint64_t thread_id; void *user_context; @@ -262,7 +270,7 @@ struct Dqn_OSHttpResponse // Synchronous HTTP response uses the TLS scratch arena whereas async // calls use their own dedicated arena. Dqn_Arena tmp_arena; - Dqn_Arena *scratch_arena; + Dqn_Arena *tmem_arena; Dqn_Str8Builder builder; 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 Dqn_Str8 Dqn_OS_EXEPath (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); // NOTE: Counters ////////////////////////////////////////////////////////////////////////////////// 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) // NOTE: File system paths ///////////////////////////////////////////////////////////////////////// -DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path); -DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path); -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_MoveFile (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_DirExists (Dqn_Str8 path, Dqn_ErrorSink *error); +DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path); +DQN_API bool Dqn_OS_FileIsOlderThan(Dqn_Str8 file, Dqn_Str8 check_against); +DQN_API bool Dqn_OS_PathDelete (Dqn_Str8 path); +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_MoveFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, 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 //////////////////////////////////////////////////////////////////////////// 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); // NOTE: R/W Entire File /////////////////////////////////////////////////////////////////////////// -DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error); -DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); -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_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...); -DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); -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, ...); +DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Arena *arena, Dqn_Str8 path, Dqn_ErrorSink *error); +#define Dqn_OS_ReadAll_TLS(...) Dqn_OS_ReadAll(Dqn_TLS_TopArena(), ##__VA_ARGS__) +DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); +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_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error); +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) // 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_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path); -DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...); -DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_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_PathConvertTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor); -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_PathConvert (Dqn_Arena *arena, Dqn_Str8 path); -DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API bool Dqn_OS_PathAddRef (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_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path); +#define Dqn_OS_PathAdd_TLS(...) Dqn_OS_PathAdd(Dqn_TLS_TopArena(), ##__VA_ARGS__) +DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...); +#define Dqn_OS_PathAddF_TLS(...) Dqn_OS_PathAddF(Dqn_TLS_TopArena(), ##__VA_ARGS__) +DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path); +DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator (Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator); +#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_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) // NOTE: [$EXEC] Dqn_OSExec //////////////////////////////////////////////////////////////////////// -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_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error); -DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice 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 cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena); +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_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error); +DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice 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 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 /////////////////////////////////////////////////////////////////// #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 void Dqn_OS_ThreadDeinit(Dqn_OSThread thread); DQN_API uint32_t Dqn_OS_ThreadID (); +DQN_API void Dqn_OS_ThreadSetTLS(Dqn_TLS *tls); #endif // !defined(DQN_NO_THREAD) // NOTE: [$HTTP] Dqn_OSHttp //////////////////////////////////////////////////////////////////////// diff --git a/dqn_os_posix.cpp b/dqn_os_posix.cpp index 5a7d30b..5b22a88 100644 --- a/dqn_os_posix.cpp +++ b/dqn_os_posix.cpp @@ -1,6 +1,8 @@ #pragma once #include "dqn.h" +#include // readdir, opendir, closedir + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -271,6 +273,12 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path) // shoddily deal with this. result.create_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; } @@ -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); if (!result) { int error_code = errno; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); 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_U64ToByteSizeStr8(scratch.arena, bytes_written, Dqn_U64ByteSizeType_Auto); + Dqn_U64ToByteSizeStr8(tmem.arena, bytes_written, Dqn_U64ByteSizeType_Auto); Dqn_ErrorSink_MakeF(error, error_code, "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; } -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_Scratch scratch = Dqn_Scratch_Get(nullptr); - bool result = true; + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + bool result = true; // TODO(doyle): Implement this without using the path indexes, it's not // necessary. See Windows implementation. Dqn_usize path_indexes_size = 0; 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--) { bool first_char = index == (copy.size - 1); char ch = copy.data[index]; @@ -467,6 +463,51 @@ DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path) 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 //////////////////////////////////////////////////////////////////////////// DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, 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; 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_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); + Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_ErrorSink_MakeF( error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8)); 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) == 1 /*count*/; if (!result) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); Dqn_Str8 buffer_size_str8 = - Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); + Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto); Dqn_ErrorSink_MakeF( 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 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) { char buffer[4096]; - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena); for (;;) { ssize_t bytes_read = read(stdout_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer)); if (bytes_read <= 0) 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); @@ -681,14 +721,13 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle, if (arena && handle.stderr_read) { char buffer[4096]; - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; + Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena); for (;;) { ssize_t bytes_read = read(stderr_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer)); if (bytes_read <= 0) 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); @@ -713,8 +752,8 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, if (cmd_line.size == 0) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ")); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(tmem.arena, cmd_line, DQN_STR8(" ")); int stdout_pipe[Dqn_OSPipeType__Count] = {}; int stderr_pipe[Dqn_OSPipeType__Count] = {}; @@ -807,7 +846,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, // NOTE: Convert the command into something suitable for execvp 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) { result.exit_code = -1; Dqn_ErrorSink_MakeF( @@ -820,7 +859,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; 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 @@ -1008,9 +1047,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex) // NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// static void *Dqn_OS_ThreadFunc_(void *user_context) { - Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *) user_context; - Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT); - thread->func(thread); + Dqn_OS_ThreadExecute_(user_context); return nullptr; } @@ -1167,12 +1204,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, response->arena = 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_Scratch scratch_ = Dqn_Scratch_Get(arena); - if (!scratch_arena) - scratch_arena = scratch_.arena; + Dqn_Arena *tmem = response->tmem_arena; + Dqn_TLSTMem tmem_ = Dqn_TLS_TMem(arena); + if (!tmem) + tmem = tmem_.arena; #if defined(DQN_PLATFORM_EMSCRIPTEN) emscripten_fetch_attr_t fetch_attribs = {}; diff --git a/dqn_os_win32.cpp b/dqn_os_win32.cpp index 58ac16c..ba66fe5 100644 --- a/dqn_os_win32.cpp +++ b/dqn_os_win32.cpp @@ -1,6 +1,7 @@ #pragma once #include "dqn.h" +/* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ @@ -15,8 +16,9 @@ // dqn_os_win32.cpp // //////////////////////////////////////////////////////////////////////////////////////////////////// +*/ -// NOTE: [$VMEM] Dqn_OSMem ////////////////////////////////////////////////////////////////////////// +// NOTE: [$VMEM] Dqn_OSMem ///////////////////////////////////////////////////////////////////////// static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect) { DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0); @@ -217,8 +219,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena) Dqn_Str8 result = {}; if (!arena) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(scratch.arena); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena); + Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(t_mem.arena); result = Dqn_Win_Str16ToStr8(arena, exe_dir16); return result; } @@ -259,8 +261,8 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path) if (!Dqn_Str8_HasData(path)) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path); WIN32_FILE_ATTRIBUTE_DATA 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)) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path); if (path16.size) { result = DeleteFileW(path16.data); if (!result) @@ -308,8 +310,8 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path) if (!Dqn_Str8_HasData(path)) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA 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) { bool result = false; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); - Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src); + Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest); int fail_if_exists = overwrite == false; result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; 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, win_error.code, "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) { bool result = false; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); - Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src); + Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest); unsigned long flags = MOVEFILE_COPY_ALLOWED; 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; 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, win_error.code, "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) { - bool result = true; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + bool result = true; + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + 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 // 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)) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA 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; } - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path); void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ access_mode, /*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); 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; 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; @@ -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) return false; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); 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( error, 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, /*LPOVERLAPPED lpOverlapped*/ nullptr); 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)); return false; } 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( error, win_error.code, @@ -554,9 +556,9 @@ DQN_API bool Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize } if (!result) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena); - Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena); + 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)); } return result; @@ -594,69 +596,106 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle, Dqn_Arena return result; } - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - DWORD exec_result = WaitForSingleObject(handle.process, INFINITE); - CloseHandle(handle.stdout_write); - CloseHandle(handle.stderr_write); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena); + Dqn_Str8Builder stdout_builder = {}; + Dqn_Str8Builder stderr_builder = {}; + if (arena) { + stdout_builder.arena = t_mem.arena; + stderr_builder.arena = t_mem.arena; + } - if (exec_result == WAIT_FAILED) { - Dqn_WinError win_error = Dqn_Win_LastError(scratch.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)); - CloseHandle(handle.process); - } else { - // NOTE: Get exit code ///////////////////////////////////////////////////////////////////// + DWORD const SLOW_WAIT_TIME_MS = 100; + DWORD const FAST_WAIT_TIME_MS = 20; + DWORD wait_ms = FAST_WAIT_TIME_MS; + DWORD exec_result = WAIT_TIMEOUT; + while (exec_result == WAIT_TIMEOUT) { + exec_result = WaitForSingleObject(handle.process, wait_ms); + 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; if (GetExitCodeProcess(handle.process, &exit_status)) { result.exit_code = exit_status; } 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; Dqn_ErrorSink_MakeF(error, result.os_error_code, "Failed to retrieve command exit code: %.*s", 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.stderr_read); + CloseHandle(handle.process); return result; } @@ -667,10 +706,10 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn if (cmd_line.size == 0) return result; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ")); - Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd_rendered); - Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(t_mem.arena, cmd_line, DQN_STR8(" ")); + Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(t_mem.arena, cmd_rendered); + Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(t_mem.arena, working_dir); // NOTE: Stdout/err security attributes //////////////////////////////////////////////////////// SECURITY_ATTRIBUTES save_std_security_attribs = {}; @@ -689,7 +728,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout)) { 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; Dqn_ErrorSink_MakeF( error, @@ -701,7 +740,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn } 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; Dqn_ErrorSink_MakeF(error, result.os_error_code, @@ -729,7 +768,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn stderr_write = stdout_write; } else { 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; Dqn_ErrorSink_MakeF( error, @@ -741,7 +780,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn } 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; Dqn_ErrorSink_MakeF(error, result.os_error_code, @@ -764,7 +803,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice cmd_line, Dqn startup_info.dwFlags |= STARTF_USESTDHANDLES; BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info); 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; Dqn_ErrorSink_MakeF(error, result.os_error_code, @@ -882,9 +921,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex) // NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// static DWORD __stdcall Dqn_OS_ThreadFunc_(void *user_context) { - Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context; - Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT); - thread->func(thread); + Dqn_OS_ThreadExecute_(user_context); return 0; } @@ -1001,7 +1038,7 @@ void Dqn_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD DWORD bytes_read = dwStatusInformationLength; if (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); if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) @@ -1050,12 +1087,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, return; 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_Scratch scratch_ = Dqn_Scratch_Get(arena); - if (!scratch_arena) { - scratch_arena = scratch_.arena; + Dqn_Arena *t_mem_arena = response->tmem_arena; + Dqn_TLSTMem t_mem_ = Dqn_TLS_TMem(arena); + if (!t_mem_arena) { + t_mem_arena = t_mem_.arena; } Dqn_WinError error = {}; @@ -1088,15 +1125,15 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, 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*/); if (!response->win32_request_connection) { error = Dqn_Win_LastError(&response->tmp_arena); return; } - Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(scratch_arena, method); - Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch_arena, path); + Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(t_mem_arena, method); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem_arena, path); response->win32_request_handle = WinHttpOpenRequest(response->win32_request_connection, method16.data, path16.data, @@ -1109,7 +1146,7 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, 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); if (!WinHttpSendRequest(response->win32_request_handle, headers16.data, @@ -1295,13 +1332,13 @@ DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8(Dqn_Arena *arena, Dqn_Str16 src) // NOTE: Windows Executable Directory ////////////////////////////////////////// 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_usize module_size = 0; wchar_t *module_path = nullptr; do { 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) return result; 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) { // 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_usize module_size = 0; wchar_t *module_path = nullptr; do { 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) return result; 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_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(scratch.arena, suffix); - Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(scratch.arena, suffix16); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena); + Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(t_mem.arena, suffix); + Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(t_mem.arena, suffix16); Dqn_Str8 result = Dqn_Win_Str16ToStr8(arena, dir16); return result; } @@ -1359,15 +1396,15 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix) Dqn_Str16 result = {}; // 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 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); - if (!scratch_w_path) + wchar_t *t_mem_w_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, desired_size, Dqn_ZeroMem_No); + if (!t_mem_w_path) 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) { // TODO(dqn): Error return result; @@ -1378,7 +1415,7 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix) return result; 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); 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) return false; - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr); Dqn_Win_FolderIteratorW wide_it = {}; Dqn_Str16 path16 = {}; 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 // character, add the glob and re-null terminate the buffer. 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 - 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 return false; } diff --git a/dqn_os_win32.h b/dqn_os_win32.h index e06b121..4684ca7 100644 --- a/dqn_os_win32.h +++ b/dqn_os_win32.h @@ -1,6 +1,7 @@ #pragma once #include "dqn.h" +/* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ @@ -15,6 +16,7 @@ // dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer // //////////////////////////////////////////////////////////////////////////////////////////////////// +*/ struct Dqn_WinError { diff --git a/dqn_string.cpp b/dqn_string.cpp index e39485b..9538af5 100644 --- a/dqn_string.cpp +++ b/dqn_string.cpp @@ -95,7 +95,7 @@ DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all) case Dqn_Str8IsAll_Hex: { 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]; result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } @@ -221,7 +221,7 @@ DQN_API Dqn_Slice Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin 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 = {}; 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++) { Dqn_Str8 find_item = find[find_index]; 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.index = 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; } -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; } @@ -403,6 +403,12 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix(Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8E 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_Str8 result = string; @@ -591,11 +597,10 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string, return result; } - Dqn_Scratch scratch = Dqn_Scratch_Get(arena); - Dqn_Str8Builder string_builder = {}; - string_builder.arena = scratch.arena; - Dqn_usize max = string.size - find.size; - Dqn_usize head = start_index; + Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena); + Dqn_Str8Builder string_builder = Dqn_Str8Builder_Init(tmem.arena); + Dqn_usize max = string.size - find.size; + Dqn_usize head = start_index; for (Dqn_usize tail = head; tail <= max; tail++) { 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 // replacements, where the entire string gets copied. 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_Str8Builder_AppendRef(&string_builder, range); - Dqn_Str8Builder_AppendRef(&string_builder, replace); + Dqn_Str8Builder_AddRef(&string_builder, range); + Dqn_Str8Builder_AddRef(&string_builder, replace); 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 } @@ -623,7 +628,7 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string, result = Dqn_Str8_Copy(arena, string); } else { 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); } @@ -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); if (result.data) result.size = size; + if (zero_mem == Dqn_ZeroMem_No) + result.data[result.size] = 0; return result; } @@ -717,18 +724,46 @@ DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Arena *arena, Dqn_Str8 string) } // NOTE: [$STRB] Dqn_Str8Builder //////////////////////////////////////////////////////////////// -DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice 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) return false; - for (Dqn_Str8 string : array) { - if (!builder || !string.data || string.size <= 0) - return false; + if (!strings || size <= 0) + return true; - Dqn_Str8Link *link = Dqn_Arena_New(builder->arena, Dqn_Str8Link, Dqn_ZeroMem_No); - if (!link) - return false; + Dqn_Str8Link *links = Dqn_Arena_NewArray(builder->arena, Dqn_Str8Link, size, Dqn_ZeroMem_No); + if (!links) + return false; + + DQN_FOR_UINDEX(index, size) { + Dqn_Str8 string = strings[index]; + Dqn_Str8Link *link = links + index; link->string = string; link->next = NULL; @@ -746,69 +781,203 @@ DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice< return true; } -DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice array) +DQN_API bool Dqn_Str8Builder_AddArrayCopy(Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size) { - for (Dqn_Str8 string : array) { - Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string); - if (!Dqn_Str8Builder_AppendRef(builder, copy)) - return false; - } - return true; -} + if (!builder) + return false; -DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args) -{ - Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args); - if (string.size == 0) + if (!strings || size <= 0) 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 array) +{ + bool result = Dqn_Str8Builder_AddArrayRef(builder, array.data, array.size); + return result; +} + +DQN_API bool Dqn_Str8Builder_AddSliceCopy(Dqn_Str8Builder *builder, Dqn_Slice 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); - bool result = Dqn_Str8Builder_AppendRef(builder, string); + bool result = Dqn_Str8Builder_AddRef(builder, string); if (!result) Dqn_Arena_TempMemEnd(temp_mem); 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_start(args, fmt); - bool result = Dqn_Str8Builder_AppendFV(builder, fmt, args); + bool result = Dqn_Str8Builder_AddFV(builder, fmt, args); va_end(args); 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 array = Dqn_Slice_Init(&string, 1); - bool result = Dqn_Str8Builder_AppendRefArray(builder, array); + Dqn_Str8 input = Dqn_Str8_Init(ptr, size); + bool result = Dqn_Str8Builder_AddRef(builder, input); 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 array = Dqn_Slice_Init(&string, 1); - bool result = Dqn_Str8Builder_AppendCopyArray(builder, array); + Dqn_Str8 input = Dqn_Str8_Init(ptr, size); + 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; } 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; if (!builder || builder->string_size <= 0 || builder->count <= 0) 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) return result; for (Dqn_Str8Link *link = builder->head; link; link = link->next) { DQN_MEMCPY(result.data + result.size, link->string.data, 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; - DQN_ASSERT(result.size == builder->string_size); + DQN_ASSERT(result.size == builder->string_size + size_for_delimiter); return result; } @@ -850,13 +1019,13 @@ DQN_API Dqn_Slice Dqn_Str8Builder_BuildSlice(Dqn_Str8Builder const *bu 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) 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) { if (link->next) { diff --git a/dqn_string.h b/dqn_string.h index ce6d4de..c27f026 100644 --- a/dqn_string.h +++ b/dqn_string.h @@ -143,88 +143,123 @@ DQN_API bool operator!= #endif // NOTE: [$STR8] Dqn_Str8 ////////////////////////////////////////////////////////////////////////// -#define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)} -#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(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)} +#define DQN_STR_FMT(string) (int)((string).size), (string).data +#define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)} -DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src); -#define Dqn_Str8_HasData(string) ((string).data && (string).size) -DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all); +DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src); +#define Dqn_Str8_HasData(string) ((string).data && (string).size) +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_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args); -DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); -DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size); -DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string); +DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); +#define Dqn_Str8_InitF_TLS(...) Dqn_Str8_InitF(Dqn_TLS_TopArena(), ##__VA_ARGS__) +DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args); +#define Dqn_Str8_InitFV_TLS(...) Dqn_Str8_InitFV(Dqn_TLS_TopArena(), ##__VA_ARGS__) +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_Advance (Dqn_Str8 string, Dqn_usize amount); -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_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_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_Slice Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode); +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_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_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_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_Slice 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_FindStr8 (Dqn_Str8 string, Dqn_Str8 find); -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_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); +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_Str8EqCase eq_case); +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); +#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_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_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_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix); -DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch); +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_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_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_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_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); -DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_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_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API Dqn_Str8 Dqn_Str8_TrimHexPrefix (Dqn_Str8 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_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_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_FileNameNoExtension (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_FileNameFromPath (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_FileExtension (Dqn_Str8 path); -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_Str8ToU64Result Dqn_Str8_ToU64 (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_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 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 void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size); #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 -// NOTE: [$FSTR] Dqn_Str8Builder /////////////////////////////////////////////////////////////////// -DQN_API bool Dqn_Str8Builder_AppendRefArray (Dqn_Str8Builder *builder, Dqn_Slice string); -DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice string); -DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args); -DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...); -DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string); -DQN_API bool Dqn_Str8Builder_AppendCopy (Dqn_Str8Builder *builder, Dqn_Str8 string); -DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena); -DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder); -DQN_API Dqn_Slice Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena); -DQN_API void Dqn_Str8Builder_PrintF (Dqn_Str8Builder const *builder); +// NOTE: [$STRB] Dqn_Str8Builder /////////////////////////////////////////////////////////////////// +DQN_API Dqn_Str8Builder Dqn_Str8Builder_Init (Dqn_Arena *arena); +#define Dqn_Str8Builder_Init_TLS() Dqn_Str8Builder_Init(Dqn_TLS_TopArena()) +DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayRef (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size); +#define Dqn_Str8Builder_InitArrayRef_TLS(...) Dqn_Str8Builder_InitArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__) +DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayCopy (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size); +#define Dqn_Str8Builder_InitArrayCopy_TLS(...) Dqn_Str8Builder_InitArrayCopy(Dqn_TLS_TopArena(), ##__VA_ARGS__) +template Dqn_Str8Builder Dqn_Str8Builder_InitCArrayRef (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]); +#define Dqn_Str8Builder_InitCArrayRef_TLS(...) Dqn_Str8Builder_InitCArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__) +template Dqn_Str8Builder Dqn_Str8Builder_InitCArrayCopy (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]); +#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 bool Dqn_Str8Builder_AddCArrayRef (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]); +template bool Dqn_Str8Builder_AddCArrayCopy (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]); +DQN_API bool Dqn_Str8Builder_AddSliceRef (Dqn_Str8Builder *builder, Dqn_Slice string); +DQN_API bool Dqn_Str8Builder_AddSliceCopy (Dqn_Str8Builder *builder, Dqn_Slice 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_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 ////////////////////////////////////////////////////////////////////// #if !defined(DQN_NO_FSTR8) template Dqn_FStr8 Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...); template Dqn_usize Dqn_FStr8_Max (Dqn_FStr8 const *string); template void Dqn_FStr8_Clear (Dqn_FStr8 *string); -template bool Dqn_FStr8_AppendFV (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list va); -template bool Dqn_FStr8_AppendF (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...); -template bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8 *string, char const *value, Dqn_usize size); -template bool Dqn_FStr8_Append (Dqn_FStr8 *string, Dqn_Str8 value); +template bool Dqn_FStr8_AddFV (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list va); +template bool Dqn_FStr8_AddF (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...); +template bool Dqn_FStr8_AddCStr8 (Dqn_FStr8 *string, char const *value, Dqn_usize size); +template bool Dqn_FStr8_Add (Dqn_FStr8 *string, Dqn_Str8 value); template Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8 const *string); template bool Dqn_FStr8_Eq (Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs, Dqn_Str8EqCase eq_case); template bool Dqn_FStr8_EqStr8 (Dqn_FStr8 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_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint); +// NOTE: [$STRB] Dqn_Str8Builder /////////////////////////////////////////////////////////////////// +template 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_Str8Builder Dqn_Str8Builder_InitCArrayCopy(Dqn_Arena *arena, Dqn_Str8 const (&array)[N]) +{ + Dqn_Str8Builder result = Dqn_Str8Builder_InitArrayCopy(arena, array, N); + return result; +} + +template bool Dqn_Str8Builder_AddCArrayRef(Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]) +{ + bool result = Dqn_Str8Builder_AddArrayRef(builder, array, N); + return result; +} + +template 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) // NOTE: [$FSTR] Dqn_FStr8 ///////////////////////////////////////////////////////////////////////// template Dqn_FStr8 Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...) @@ -265,7 +325,7 @@ template Dqn_FStr8 Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *f if (fmt) { va_list args; va_start(args, fmt); - Dqn_FStr8_AppendFV(&result, fmt, args); + Dqn_FStr8_AddFV(&result, fmt, args); va_end(args); } return result; @@ -282,7 +342,7 @@ template void Dqn_FStr8_Clear(Dqn_FStr8 *string) *string = {}; } -template bool Dqn_FStr8_AppendFV(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list args) +template bool Dqn_FStr8_AddFV(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list args) { bool result = false; if (!string || !fmt) @@ -299,19 +359,19 @@ template bool Dqn_FStr8_AppendFV(Dqn_FStr8 *string, DQN_FMT_ATT return result; } -template bool Dqn_FStr8_AppendF(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...) +template bool Dqn_FStr8_AddF(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...) { bool result = false; if (!string || !fmt) return result; va_list args; va_start(args, fmt); - result = Dqn_FStr8_AppendFV(string, fmt, args); + result = Dqn_FStr8_AddFV(string, fmt, args); va_end(args); return result; } -template bool Dqn_FStr8_AppendCStr8(Dqn_FStr8 *string, char const *src, Dqn_usize size) +template bool Dqn_FStr8_AddCStr8(Dqn_FStr8 *string, char const *src, Dqn_usize size) { DQN_ASSERT(string->size <= N); bool result = false; @@ -326,9 +386,9 @@ template bool Dqn_FStr8_AppendCStr8(Dqn_FStr8 *string, char con return result; } -template bool Dqn_FStr8_Append(Dqn_FStr8 *string, Dqn_Str8 src) +template bool Dqn_FStr8_Add(Dqn_FStr8 *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; } diff --git a/dqn_thread_context.cpp b/dqn_thread_context.cpp deleted file mode 100644 index 15ddf2c..0000000 --- a/dqn_thread_context.cpp +++ /dev/null @@ -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); -} diff --git a/dqn_thread_context.h b/dqn_thread_context.h deleted file mode 100644 index 855913f..0000000 --- a/dqn_thread_context.h +++ /dev/null @@ -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); diff --git a/dqn_tls.cpp b/dqn_tls.cpp new file mode 100644 index 0000000..fef4fac --- /dev/null +++ b/dqn_tls.cpp @@ -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; +} diff --git a/dqn_tls.h b/dqn_tls.h new file mode 100644 index 0000000..94aae6f --- /dev/null +++ b/dqn_tls.h @@ -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(); diff --git a/dqn_type_info.h b/dqn_type_info.h index 11a891c..48bef0e 100644 --- a/dqn_type_info.h +++ b/dqn_type_info.h @@ -30,6 +30,7 @@ struct Dqn_TypeField { uint16_t index; Dqn_Str8 name; + Dqn_Str8 label; Dqn_isize value; Dqn_usize offset_of; Dqn_usize size_of; diff --git a/dqn_unit_tests.cpp b/dqn_unit_tests.cpp index a869934..496f939 100644 --- a/dqn_unit_tests.cpp +++ b/dqn_unit_tests.cpp @@ -3,7 +3,7 @@ #include -#if !defined(__clang__) + // NOTE: Taken from MSDN __cpuid example implementation // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170 #include @@ -110,7 +110,7 @@ private: for (int i = 0; i <= nIds_; ++i) { __cpuidex(cpui.data(), i, 0); - data_.push_back(cpui); + data_[dataSize_++] = cpui; } // Capture vendor string @@ -154,7 +154,7 @@ private: for (int i = 0x80000000; i <= nExIds_; ++i) { __cpuidex(cpui.data(), i, 0); - extdata_.push_back(cpui); + extdata_[extdataSize_++] = cpui; } // load bitset with flags for function 0x80000001 @@ -186,14 +186,15 @@ private: std::bitset<32> f_7_ECX_; std::bitset<32> f_81_ECX_; std::bitset<32> f_81_EDX_; - std::vector> data_; - std::vector> extdata_; + std::array, 512> data_{}; + size_t dataSize_ = 0; + std::array, 512> extdata_{}; + size_t extdataSize_ = 0; }; }; // Initialize static member data const Dqn_RefImplCPUReport::Dqn_RefImplCPUReport_Internal Dqn_RefImplCPUReport::CPU_Rep; -#endif // !defined(__clang__) #if 0 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_GROUP(test, "Dqn_Base") { - // TODO(doyle): cpuid refimpl doesn't work on clang - #if !defined(__clang__) DQN_UTEST_TEST("Query CPUID") { 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_XSAVE) == Dqn_RefImplCPUReport::XSAVE()); #endif + } - #endif // !defined(__clang__) } return test; } @@ -427,7 +426,7 @@ static Dqn_UTest Dqn_Test_Arena() 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_GROUP(test, "Dqn_Bin") { DQN_UTEST_TEST("Convert 0x123") { @@ -487,23 +486,23 @@ static Dqn_UTest Dqn_Test_Bin() uint32_t number = 0xd095f6; 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)); } number = 0xf6ed00; 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_Str8 hex = DQN_STR8("0xf6ed00"); 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_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")), "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_GROUP(test, "Dqn_DSMap") { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); { Dqn_Arena arena = {}; uint32_t const MAP_SIZE = 64; - Dqn_DSMap map = Dqn_DSMap_Init(&arena, MAP_SIZE); + Dqn_DSMap map = Dqn_DSMap_Init(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil); DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); }; 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; } - Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(scratch.arena); + Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(tmem.arena); Dqn_Arena arena = {}; uint32_t const MAP_SIZE = 64; - Dqn_DSMap map = Dqn_DSMap_Init(&arena, MAP_SIZE); + Dqn_DSMap map = Dqn_DSMap_Init(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil); DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); }; 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 grow_threshold = map_start_size * 3 / 4; for (; map.occupied != grow_threshold; value++) { - uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); - Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); + Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key).value); Dqn_DSMapResult make_result = {}; if (test_type == DSMapTestType_Set) { @@ -895,8 +893,7 @@ static Dqn_UTest Dqn_Test_DSMap() DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value); { // 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_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); + Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value); Dqn_DSMapResult make_result = {}; if (test_type == DSMapTestType_Set) { make_result = Dqn_DSMap_Set(&map, key, value); @@ -924,7 +921,7 @@ static Dqn_UTest Dqn_Test_DSMap() // NOTE: Validate each slot value 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)); if (test_type == DSMapTestType_Set) { DQN_UTEST_ASSERT(&test, slot->value == value_test); @@ -945,9 +942,7 @@ static Dqn_UTest Dqn_Test_DSMap() uint64_t value = 0; uint64_t shrink_threshold = map.size * 1 / 4; for (; map.occupied != shrink_threshold; value++) { - uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); - Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); - + Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key).value); Dqn_DSMap_Erase(&map, key); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&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.occupied == start_map_occupied - value); - { // 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_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); + { // NOTE: One more item should cause the table to shrink by 2x + Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value); Dqn_DSMap_Erase(&map, key); value++; @@ -971,12 +965,12 @@ static Dqn_UTest Dqn_Test_DSMap() 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++) { // NOTE: Generate the key 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 Dqn_DSMapResult 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 - uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); - Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); + Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key).value); Dqn_DSMap_Erase(&map, key); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key).value); @@ -1016,12 +1009,12 @@ static Dqn_UTest Dqn_Test_FStr8() DQN_UTEST_GROUP(test, "Dqn_FStr8") { DQN_UTEST_TEST("Append too much fails") { 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_FStr8<4> str = {}; - DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AppendF(&str, "abcde")); + DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AddF(&str, "abcde")); } } return test; @@ -1049,8 +1042,8 @@ static Dqn_UTest Dqn_Test_Fs() DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE)); // NOTE: Read step - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena, nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(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, 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)); @@ -1301,8 +1294,8 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] = void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 input_hex = Dqn_BytesToHex(scratch.arena, input.data, input.size); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 input_hex = Dqn_BytesToHex(tmem.arena, input.data, input.size); switch(hash_type) { @@ -1536,8 +1529,8 @@ static Dqn_UTest Dqn_Test_OS() } DQN_UTEST_TEST("Query executable directory") { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 result = Dqn_OS_EXEDir(tmem.arena); 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)); } @@ -1709,8 +1702,8 @@ static Dqn_UTest Dqn_Test_Str8() } DQN_UTEST_TEST("Initialise with format string") { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 string = Dqn_Str8_InitF(scratch.arena, "%s", "AB"); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + 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.data[0] == 'A', "string[0]: %c", string.data[0]); 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_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); 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.data[0] == 'A', "copy[0]: %c", copy.data[0]); 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_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No); + Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr); + Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, 2, Dqn_ZeroMem_No); 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_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); 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.index == 0); 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_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); 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.index == 2); DQN_UTEST_ASSERT(&test, result.match.data[0] == '6'); @@ -2175,23 +2168,23 @@ static Dqn_UTest Dqn_Test_Win() { Dqn_UTest test = {}; 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_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1}; 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_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_TEST("Str16 to Str8: Null terminates string") { 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 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_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); char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; @@ -2259,7 +2252,7 @@ void Dqn_Test_RunSuite() int main(int argc, char *argv[]) { (void)argv; (void)argc; - Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures); + Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures); Dqn_Test_RunSuite(); return 0; } diff --git a/dqn_win32.h b/dqn_win32.h index 46df8dd..9a7f808 100644 --- a/dqn_win32.h +++ b/dqn_win32.h @@ -1,6 +1,7 @@ #pragma once #include "dqn.h" +/* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ @@ -15,11 +16,15 @@ // dqn_win32.h -- Windows replacement header // //////////////////////////////////////////////////////////////////////////////////////////////////// +*/ #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #pragma comment(lib, "bcrypt") #pragma comment(lib, "winhttp") #pragma comment(lib, "dbghelp") + #pragma comment(lib, "comdlg32") + #pragma comment(lib, "pathcch") + #pragma comment(lib, "shlwapi") #endif #if defined(DQN_NO_WIN32_MIN_HEADER) || defined(_INC_WINDOWS) @@ -27,6 +32,8 @@ #include // LONG #include // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc #include // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc + #include // PathRelativePathTO + #include // PathCchCanonicalizeEx #include // WinHttp* #include #else @@ -35,6 +42,7 @@ // NOTE: basetsd.h ///////////////////////////////////////////////////////////////////////////// typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; + typedef unsigned __int64 UINT_PTR, *PUINT_PTR; typedef ULONG_PTR SIZE_T, *PSIZE_T; typedef __int64 LONG_PTR, *PLONG_PTR; typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; @@ -47,17 +55,21 @@ }; typedef struct HINSTANCE__ *HINSTANCE; - typedef unsigned long DWORD; - typedef int BOOL; - typedef int INT; - typedef unsigned long ULONG; - typedef unsigned int UINT; - typedef unsigned short WORD; - typedef unsigned char BYTE; - typedef unsigned char UCHAR; - typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ - typedef void * HANDLE; - typedef HANDLE HLOCAL; + typedef unsigned long DWORD; + typedef int BOOL; + typedef int INT; + typedef unsigned long ULONG; + typedef unsigned int UINT; + typedef unsigned short WORD; + typedef unsigned char BYTE; + typedef unsigned char UCHAR; + typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ + typedef void * HANDLE; + typedef HANDLE HLOCAL; + + typedef unsigned __int64 WPARAM; + typedef LONG_PTR LPARAM; + typedef LONG_PTR LRESULT; #define MAX_PATH 260 @@ -85,6 +97,7 @@ typedef wchar_t WCHAR; // wc, 16-bit UNICODE character typedef CHAR * NPSTR, *LPSTR, *PSTR; typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR; + typedef long HRESULT; // NOTE: VirtualAlloc: Allocation Type #define MEM_RESERVE 0x00002000 @@ -130,6 +143,9 @@ #define STATUS_WAIT_0 ((DWORD )0x00000000L) #define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) + #define S_OK ((HRESULT)0L) + #define S_FALSE ((HRESULT)1L) + typedef union _ULARGE_INTEGER { struct { DWORD LowPart; @@ -1047,12 +1063,11 @@ 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) HANDLE __stdcall CreateThread(SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId); + __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) DWORD __stdcall GetCurrentThreadId(VOID); __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 //////////////////////////////////////////////////////////////////////// @@ -1077,6 +1092,7 @@ 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 ShellExecuteW(HWND hwnd, WCHAR const *lpOperation, WCHAR const *lpFile, WCHAR const *lpParameters, WCHAR const *lpDirectory, INT nShowCmd); } // NOTE: um/debugapi.h ///////////////////////////////////////////////////////////////////////// @@ -1088,14 +1104,150 @@ // NOTE: um/namedpipeapi.h ///////////////////////////////////////////////////////////////////// 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" { - __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 #endif // !defined(_INC_WINDOWS)