diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index 11a307b..785836b 100644 --- a/Single-Header/dn_single_header.cpp +++ b/Single-Header/dn_single_header.cpp @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-07-31 22:40:42 +// Generated by the DN single header generator 2026-09-29 22:28:12 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_WITH_TESTS 1 @@ -7,6 +7,7 @@ // #define DN_WITH_NET_CURL 1 // #define DN_ARENA_TEMP_MEM_UAF_GUARD 1 // #include "dn.h" +// #include // #endif #if DN_STR8_AVX512F @@ -52,8 +53,15 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) } { - os->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(4), DN_MemFlags_NoAllocTrack, DN_OS_HeapInitDefault()); - os->arena = DN_ArenaFromMemList(&os->mem); + // NOTE: We initially exclude the OS allocation from the mem debugger because when we allocate + // memory, it hits the debug hook and tries to register the allocation. The memory debugger + // on `DN_OS_W32Init` to create stack traces which has not run yet. So we exclude it for now + // and add it into the memory debugger manually after the fact. + DN_Heap heap = DN_OS_HeapInitDefault(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + + os->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(4), DN_MemFlags_Nil, heap, "DN OS MemList"); + os->arena = DN_ArenaFromMemList(&os->mem); #if defined(DN_PLATFORM_WIN32) os->platform_context = DN_ArenaNew(&os->arena, DN_OSW32Core, DN_ZMem_Yes); @@ -62,28 +70,30 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) #endif #if defined(DN_PLATFORM_WIN32) - DN_OSW32Core *w32 = DN_Cast(DN_OSW32Core *) os->platform_context; - InitializeCriticalSection(&w32->sync_primitive_free_list_mutex); - - QueryPerformanceFrequency(&w32->qpc_frequency); - HMODULE module = LoadLibraryA("kernel32.dll"); - if (module) { - w32->set_thread_description = DN_Cast(DN_OSW32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription"); - FreeLibrary(module); - } - - // NOTE: win32 bcrypt - wchar_t const BCRYPT_ALGORITHM[] = L"RNG"; - long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/); - if (w32->bcrypt_rng_handle && init_status == 0) - w32->bcrypt_init_success = true; - else - DN_LogErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status); + DN_OS_W32Init(DN_Cast(DN_OSW32Core *) os->platform_context); #else DN_OS_PosixInit(DN_Cast(DN_OSPosixCore *)os->platform_context); #endif } + // NOTE: Mem Debugger + { + #if DN_MEM_DEBUGGER + // NOTE: Setup the mem debugger table with allocation tracking turned off on because we're + // allocating before we've setup the table to track allocations itself (and would otherwise cause an recursive loop). + DN_Heap heap = DN_OS_HeapInitBasic(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + DN_CallSite alloc_call_site = DN_CallSiteNowNamed("DN Memory Debugger Hash Table"); + DN_HTableInitArgs table_args = DN_HTableInitArgsDefault(DN_MemDebuggerKV, dn->mem_debugger.alloc_table_kvs, hash, key, value); + dn->mem_debugger.alloc_table = DN_HTableInitHeapAssert(table_args, heap); + + // NOTE: Unset the exclusion flag and manually add the pointers we allocator + os->mem.heap.flags &= ~DN_HeapFlags_ExcludeFromMemDebugger; + for (DN_MemBlock *block = os->arena.mem->curr; block; block = block->prev) + DN_MemDebuggerAlloc(&dn->mem_debugger, block, block->reserve, /*alloc_can_leak=*/true, alloc_call_site); + #endif + } + os->cpu_report = DN_CPUGetReport(); #define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_Str8Lit(#label)}; @@ -93,20 +103,12 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) #endif // NOTE: Initialise thread context - DN_TcInitFromHeap(&dn->main_tc, DN_OS_ThreadID(), args, DN_OS_HeapInitDefault()); + DN_Heap tc_heap = DN_OS_HeapInitDefault(); + tc_heap.flags |= DN_HeapFlags_MemDebuggerCanLeakPtr; + DN_TcInitFromHeap(&dn->main_tc, DN_OS_ThreadID(), args, tc_heap); DN_TcEquip(&dn->main_tc); } - if (DN_BitIsSet(flags, DN_InitFlags_LeakTracker)) { - DN_Assert(dn->os_init); - #if DN_WITH_OS - // NOTE: Setup the allocation table with allocation tracking turned off on - // the arena we're using to initialise the table. - DN_HTableInitArgs table_args = DN_HTableInitArgsDefault(DN_LeakTrackerKV, dn->leak.alloc_table_kvs, hash, key, value); - dn->leak.alloc_table = DN_HTableInitHeapAssert(table_args, DN_OS_HeapInitBasic()); - #endif - } - // NOTE: Print out init features char buf[4096]; DN_USize buf_size = 0; @@ -157,7 +159,7 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) } #endif - #if defined(DN_LEAK_TRACKING) + #if defined(DN_MEM_DEBUGGER) DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " Allocation leak tracing\n"); #endif @@ -1078,7 +1080,7 @@ DN_API DN_F32 DN_EpsilonClampF32(DN_F32 value, DN_F32 target, DN_F32 epsilon) return result; } -static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_Heap heap) +static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, DN_Heap heap, DN_CallSite call_site) { DN_MemBlock *result = nullptr; switch (heap.type) { @@ -1086,14 +1088,18 @@ static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool tra break; case DN_HeapType_Basic: { - DN_AssertF(reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", reserve, DN_ARENA_HEADER_SIZE); - result = DN_Cast(DN_MemBlock *) heap.basic_alloc(reserve); + DN_USize real_reserve = reserve; + if (reserve == 0) + real_reserve = DN_ARENA_RESERVE_SIZE; + + DN_AssertF(real_reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", real_reserve, DN_ARENA_HEADER_SIZE); + result = DN_Cast(DN_MemBlock *) DN_HeapAllocCallSite(&heap, real_reserve, 0, DN_HeapAllocFlag_Nil, call_site); if (!result) return result; result->used = DN_ARENA_HEADER_SIZE; - result->commit = reserve; - result->reserve = reserve; + result->commit = real_reserve; + result->reserve = real_reserve; } break; case DN_HeapType_Virtual: { @@ -1110,25 +1116,15 @@ static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool tra real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve); DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%zu < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve); - DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No; - result = DN_Cast(DN_MemBlock *) heap.virtual_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite); - if (!result) - return result; - - if (mem_commit == DN_MemCommit_No && !heap.virtual_commit(result, real_commit, DN_MemPage_ReadWrite)) { - heap.virtual_release(result, real_reserve); - return result; + result = DN_Cast(DN_MemBlock *) DN_HeapAllocCallSite(&heap, real_reserve, real_commit, DN_HeapAllocFlag_Nil, call_site); + if (result) { + result->used = DN_ARENA_HEADER_SIZE; + result->commit = real_commit; + result->reserve = real_reserve; } - - result->used = DN_ARENA_HEADER_SIZE; - result->commit = real_commit; - result->reserve = real_reserve; } break; } - if (track_alloc && result) - DN_LeakTrackAlloc(&g_dn_->leak, result, result->reserve, alloc_can_leak); - return result; } @@ -1141,11 +1137,9 @@ static bool DN_ArenaHasPoison_(DN_MemFlags flags) return result; } -static DN_MemBlock *DN_MemBlockFromHeapFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +static DN_MemBlock *DN_MemBlockFromHeapFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { - bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; - bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; - DN_MemBlock *result = DN_MemBlockFromHeap_(reserve, commit, track_alloc, alloc_can_leak, heap); + DN_MemBlock *result = DN_MemBlockFromHeap_(reserve, commit, heap, call_site); if (result && DN_ArenaHasPoison_(flags)) { char *poison = DN_Cast(char *)result + result->used; DN_USize poison_size = result->commit - result->used; @@ -1215,43 +1209,46 @@ DN_API DN_Heap DN_HeapInitVirtual(DN_U32 page_size, DN_HeapVirtualReserveFunc *v return result; } -DN_API void* DN_HeapAlloc(DN_Heap *heap, DN_USize reserve, DN_USize commit) +DN_API void *DN_HeapAllocCallSite(DN_Heap *heap, DN_USize reserve, DN_USize commit, DN_HeapAllocFlag flags, DN_CallSite call_site) { void *result = nullptr; switch (heap->type) { case DN_HeapType_Nil: break; - case DN_HeapType_Basic: result = heap->basic_alloc(reserve); break; + case DN_HeapType_Basic: { + result = heap->basic_alloc(reserve); + } break; case DN_HeapType_Virtual: { - DN_AssertF(heap->virtual_page_size, - "Page size must be set to a non-zero, power of two value. Virtual memory " - "functions are usually initialised by values obtained during initialisation, has " - "DN_Init() been called yet?"); - DN_Assert(DN_IsPowerOfTwo(heap->virtual_page_size)); + DN_MemCommit mem_commit = reserve == commit ? DN_MemCommit_Yes : DN_MemCommit_No; + result = DN_Cast(DN_MemBlock *) heap->virtual_reserve(reserve, mem_commit, DN_MemPage_ReadWrite); + if (result) { + bool commit_success = true; + if (mem_commit == DN_MemCommit_No && commit > 0) { + // NOTE: If mem-commit is yes, then the entire block of memory that was reserved is + // committed in the same syscall as the allocation itself as an optimisation. If + // mem-commit is no, which is this branch, then we commit just the range that the user + // requested in `commit`. + commit_success = heap->virtual_commit(result, commit, DN_MemPage_ReadWrite); + } - DN_USize const page_size = heap->virtual_page_size; - DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE; - DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE; - real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size); - real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve); - DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%zu < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve); - - DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No; - result = DN_Cast(DN_MemBlock *) heap->virtual_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite); - if (!result) - return result; - - if (mem_commit == DN_MemCommit_No && !heap->virtual_commit(result, real_commit, DN_MemPage_ReadWrite)) { - heap->virtual_release(result, real_reserve); - return result; + if (!commit_success) { + heap->virtual_release(result, reserve); + result = nullptr; + } } } break; } if (result) { - heap->bytes_alloc += reserve; + heap->bytes_alloc += reserve; heap->bytes_alloc_total += reserve; + if (DN_BitIsNotSet(flags, DN_HeapAllocFlag_MemDebuggerExcludePtr) && + DN_BitIsNotSet(heap->flags, DN_HeapFlags_ExcludeFromMemDebugger)) { + (void)call_site; + bool ptr_can_leak = flags & DN_HeapAllocFlag_MemDebuggerCanLeakPtr || heap->flags & DN_HeapFlags_MemDebuggerCanLeakPtr; + DN_MemDebuggerAlloc(&g_dn_->mem_debugger, result, reserve, ptr_can_leak, call_site); + } } return result; } @@ -1260,6 +1257,10 @@ DN_API void DN_HeapDealloc(DN_Heap *heap, void *ptr, DN_USize size) { heap->bytes_alloc -= size; heap->bytes_freed += size; + + if (DN_BitIsNotSet(heap->flags, DN_HeapFlags_ExcludeFromMemDebugger)) + DN_MemDebuggerDealloc(&g_dn_->mem_debugger, ptr); + if (heap->type == DN_HeapType_Basic) heap->basic_dealloc(ptr); else @@ -1281,18 +1282,18 @@ DN_API DN_MemList DN_MemListFromBuffer(void *buffer, DN_USize size, DN_MemFlags DN_ASanPoisonMemoryRegion(DN_Cast(char *) block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE); DN_MemList result = {}; - result.flags = flags | DN_MemFlags_NoGrow | DN_MemFlags_NoAllocTrack | DN_MemFlags_AllocCanLeak | DN_MemFlags_UserBuffer; + result.flags = flags | DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer; result.curr = block; DN_MemListOnNewBlock_(&result, result.curr); return result; } -DN_API DN_MemList DN_MemListFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +DN_API DN_MemList DN_MemListFromHeapCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { DN_MemList result = {}; result.heap = heap; result.flags |= flags | DN_MemFlags_Heap; - result.curr = DN_MemBlockFromHeapFlags_(reserve, commit, flags, heap); + result.curr = DN_MemBlockFromHeapFlags_(reserve, commit, flags, heap, call_site); DN_MemListOnNewBlock_(&result, result.curr); return result; } @@ -1300,12 +1301,8 @@ DN_API DN_MemList DN_MemListFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags static void DN_MemBlockDeinit_(DN_MemList *mem, DN_MemBlock *block) { DN_USize release_size = block->reserve; - if (DN_BitIsNotSet(mem->flags, DN_MemFlags_NoAllocTrack)) - DN_LeakTrackDealloc(&g_dn_->leak, block); - if (DN_ArenaHasPoison_(mem->flags)) DN_ASanUnpoisonMemoryRegion(block, block->commit); - if (mem->flags & DN_MemFlags_Heap) DN_HeapDealloc(&mem->heap, block, release_size); } @@ -1368,8 +1365,8 @@ DN_API bool DN_MemListGrow(DN_MemList *mem, DN_U64 reserve, DN_U64 commit) if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) return false; - bool result = false; - DN_MemBlock *new_block = DN_MemBlockFromHeapFlags_(reserve, commit, mem->flags, mem->heap); + bool result = false; + DN_MemBlock *new_block = DN_MemBlockFromHeapFlags_(reserve, commit, mem->flags, mem->heap, DN_CallSiteNowNamed("DN MemList Grow")); if (new_block) { result = true; new_block->prev = mem->curr; @@ -1386,7 +1383,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem return nullptr; if (!mem->curr) { - mem->curr = DN_MemBlockFromHeapFlags_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, mem->flags, mem->heap); + mem->curr = DN_MemBlockFromHeapFlags_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, mem->flags, mem->heap, DN_CallSiteNowNamed("DN MemList Lazy Bootstrap")); DN_MemListOnNewBlock_(mem, mem->curr); } @@ -1688,9 +1685,9 @@ DN_API DN_Arena DN_ArenaFromMemList(DN_MemList *mem) return result; } -DN_API DN_Arena DN_ArenaFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +DN_API DN_Arena DN_ArenaFromHeapCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { - DN_MemList mem = DN_MemListFromHeap(reserve, commit, flags, heap); + DN_MemList mem = DN_MemListFromHeapCallSite(reserve, commit, flags, heap, call_site); DN_Arena result = {}; result.flags |= DN_ArenaFlags_OwnsMemList; result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); @@ -2176,19 +2173,19 @@ DN_API DN_TcInitArgs DN_TcInitArgsDefault() DN_API void DN_TcInitFromHeap(DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap) { DN_Assert(args.temp_count <= DN_ArrayCountU(tc->temp_arenas)); - DN_MemList main_mem_stack = DN_MemListFromHeap(args.main_reserve, args.main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList main_mem_stack = DN_MemListFromHeap(args.main_reserve, args.main_commit, DN_MemFlags_Nil, heap, "DN Thread Context Main MemList"); DN_MemList *main_mem = DN_MemListNewCopy(&main_mem_stack, DN_MemList, &main_mem_stack); DN_Arena *main_arena = DN_MemListNewZ(main_mem, DN_Arena); *main_arena = DN_ArenaFromMemList(main_mem); DN_Arena *temp_arenas = DN_MemListNewArrayZ(main_mem, DN_Arena, args.temp_count); for (DN_ForIndexU(index, args.temp_count)) { - DN_MemList temp_mem_stack = DN_MemListFromHeap(args.temp_reserve, args.temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList temp_mem_stack = DN_MemListFromHeap(args.temp_reserve, args.temp_commit, DN_MemFlags_Nil, heap, "DN Thread Context Temp MemList"); DN_MemList *temp_mem = DN_MemListNewCopy(main_mem, DN_MemList, &temp_mem_stack); temp_arenas[index] = DN_ArenaFromMemList(temp_mem); } - DN_MemList err_sink_mem_stack = DN_MemListFromHeap(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList err_sink_mem_stack = DN_MemListFromHeap(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_Nil, heap, "DN ThreadContext Err MemList"); DN_MemList *err_sink_mem = DN_MemListNewCopy(main_mem, DN_MemList, &err_sink_mem_stack); DN_Arena *err_sink_arena = DN_MemListNewZ(main_mem, DN_Arena); *err_sink_arena = DN_ArenaFromMemList(err_sink_mem); @@ -4005,13 +4002,65 @@ DN_API DN_Str8 DN_Str8PadNewLinesArena(DN_Str8 string, DN_Str8 pad_string, DN_Ar return result; } -DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFlags flags) +DN_API bool DN_Utf32IsAlphabet(DN_U32 codepoint) +{ + bool result = (codepoint >= 'A' && codepoint <= 'Z') || (codepoint >= 'a' && codepoint <= 'z'); + return result; +} + +DN_API bool DN_Utf32IsDigit(DN_U32 codepoint) +{ + bool result = (codepoint >= '0' && codepoint <= '9'); + return result; +} + +DN_API bool DN_Utf32IsAlphaNum(DN_U32 codepoint) +{ + bool result = DN_Utf32IsAlphabet(codepoint) || DN_Utf32IsDigit(codepoint); + return result; +} + +DN_API bool DN_Utf32IsWhitespace(DN_U32 codepoint) +{ + bool result = (codepoint == ' ' || codepoint == '\t' || codepoint == '\n' || codepoint == '\r'); + return result; +} + +DN_API bool DN_Utf32IsHex(DN_U32 codepoint) +{ + bool result = ((codepoint >= 'a' && codepoint <= 'f') || (codepoint >= 'A' && codepoint <= 'F') || (codepoint >= '0' && codepoint <= '9')); + return result; +} + +DN_API bool DN_Utf32IsWordCharacter(DN_U32 codepoint) +{ + bool result = DN_Utf32IsAlphaNum(codepoint) || codepoint == '_'; + return result; +} + +DN_API DN_U32 DN_Utf32ToLower(DN_U32 codepoint) +{ + DN_U32 result = codepoint; + if (result >= 'A' && result <= 'Z') + result += 'a' - 'A'; + return result; +} + +DN_API DN_U32 DN_Utf32ToUpper(DN_U32 codepoint) +{ + DN_U32 result = codepoint; + if (result >= 'a' && result <= 'z') + result -= 'a' - 'A'; + return result; +} + +DN_API DN_USize DN_Utf32CodepointCountFromUtf8(DN_Str8 str, DN_CodepointCountFlags flags) { DN_USize result = 0; if (DN_BitIsNotSet(flags, DN_CodepointCountFlags_SkipAnsiCode)) { - DN_UTF8DecodeIterator it = {}; - while (DN_UTF8DecodeIterate(&it, str)) + DN_Utf32FromUtf8Iterator it = {}; + while (DN_Utf32FromUtf8Iterate(&it, DN_Utf32IterateDir_Forwards, str)) ; result = it.codepoint_index; } else { @@ -4035,7 +4084,7 @@ DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFla continue; } - DN_UTF8DecodeResult decode = DN_UTF8Decode(DN_Str8FromPtr(p, end - p)); + DN_Utf32FromResult decode = DN_Utf32FromUtf8Stream(DN_Str8FromPtr(p, end - p)); if (!decode.success) break; p = decode.remaining.data; @@ -4132,7 +4181,7 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num for (DN_USize i = 0; i < num_cols; i++) { for (DN_USize j = 0; j < num_rows; j++) { DN_USize index = j * num_cols + i; - col_widths[i] = DN_Max(col_widths[i], (DN_U16)DN_USizeCodepointCountFromUTF8(rows[index], DN_CodepointCountFlags_SkipAnsiCode)); + col_widths[i] = DN_Max(col_widths[i], (DN_U16)DN_Utf32CodepointCountFromUtf8(rows[index], DN_CodepointCountFlags_SkipAnsiCode)); } } @@ -4151,7 +4200,7 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num DN_USize index = (i * num_cols) + j; DN_Str8 item = rows[index]; DN_Str8BuilderAppendF(&builder, " %.*s", DN_Str8PrintFmt(item)); - DN_USize item_width = DN_USizeCodepointCountFromUTF8(item, DN_CodepointCountFlags_SkipAnsiCode); + DN_USize item_width = DN_Utf32CodepointCountFromUtf8(item, DN_CodepointCountFlags_SkipAnsiCode); for (DN_USize k = 0; k < col_widths[j] - item_width; k++) DN_Str8BuilderAppendF(&builder, " "); DN_Str8BuilderAppendF(&builder, " |"); @@ -4660,7 +4709,7 @@ DN_API DN_Str8Builder DN_Str8BuilderFromBuilder(DN_Arena *arena, DN_Str8Builder return result; } -DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_Str8BuilderAdd add) +DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add) { if (!builder) return false; @@ -4673,7 +4722,7 @@ DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *st if (!links) return false; - if (add == DN_Str8BuilderAdd_Append) { + if (add == DN_AddType_Append) { for (DN_ForIndexU(index, count)) { DN_Str8 string = strings[index]; DN_Str8Link *link = links + index; @@ -4688,7 +4737,7 @@ DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *st builder->string_size += string.count; } } else { - DN_Assert(add == DN_Str8BuilderAdd_Prepend); + DN_Assert(add == DN_AddType_Prepend); DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop for (DN_USize index = count - 1; index < count; index--) { @@ -4707,7 +4756,7 @@ DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *st return true; } -DN_API bool DN_Str8BuilderAddArrayCopy(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_Str8BuilderAdd add) +DN_API bool DN_Str8BuilderAddArrayCopy(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add) { if (!builder) return false; @@ -4733,7 +4782,7 @@ DN_API bool DN_Str8BuilderAddArrayCopy(DN_Str8Builder *builder, DN_Str8 const *s return result; } -DN_API bool DN_Str8BuilderAddFV(DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_FMT_ATTRIB char const *fmt, va_list args) +DN_API bool DN_Str8BuilderAddFV(DN_Str8Builder *builder, DN_AddType add, DN_FMT_ATTRIB char const *fmt, va_list args) { DN_Str8 string = DN_Str8FmtVArena(builder->arena, fmt, args); DN_U64 arena_p = DN_MemListPos(builder->arena->mem); @@ -4745,13 +4794,13 @@ DN_API bool DN_Str8BuilderAddFV(DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_API bool DN_Str8BuilderAppendRef(DN_Str8Builder *builder, DN_Str8 string) { - bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_Str8BuilderAdd_Append); + bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_AddType_Append); return result; } DN_API bool DN_Str8BuilderAppendCopy(DN_Str8Builder *builder, DN_Str8 string) { - bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_Str8BuilderAdd_Append); + bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_AddType_Append); return result; } @@ -4840,13 +4889,13 @@ DN_API bool DN_Str8BuilderAppendBuilderCopy(DN_Str8Builder *dest, DN_Str8Builder DN_API bool DN_Str8BuilderPrependRef(DN_Str8Builder *builder, DN_Str8 string) { - bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_Str8BuilderAdd_Prepend); + bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_AddType_Prepend); return result; } DN_API bool DN_Str8BuilderPrependCopy(DN_Str8Builder *builder, DN_Str8 string) { - bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_Str8BuilderAdd_Prepend); + bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_AddType_Prepend); return result; } @@ -5097,9 +5146,7 @@ DN_API DN_Str8 DN_Str8FmtOsPathArena(DN_Arena *arena, DN_FMT_ATTRIB char const * return result; } - -// NOTE: DN_UTF -DN_API int DN_UTF8Encode(DN_U8 utf8[4], DN_U32 codepoint) +DN_API DN_USize DN_Utf8FromUtf32(DN_U8 utf8[4], DN_U32 codepoint) { // NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/ // ----------------------------------------+----------------------------+--------------------+ @@ -5140,96 +5187,7 @@ DN_API int DN_UTF8Encode(DN_U8 utf8[4], DN_U32 codepoint) return 0; } -DN_API DN_UTF8DecodeResult DN_UTF8Decode(DN_Str8 stream) -{ - DN_UTF8DecodeResult result = {}; - result.remaining = stream; - if (stream.count <= 0) - return result; - - DN_U8 b0 = DN_Cast(DN_U8)stream.data[0]; - DN_U8 b1 = DN_Cast(DN_U8)(stream.count >= 2 ? stream.data[1] : 0); - DN_U8 b2 = DN_Cast(DN_U8)(stream.count >= 3 ? stream.data[2] : 0); - DN_U8 b3 = DN_Cast(DN_U8)(stream.count >= 4 ? stream.data[3] : 0); - - if ((b0 & 0b1000'0000) == 0) { - result.codepoint = b0; - result.success = true; - result.remaining = DN_Str8FromPtr(stream.data + 1, stream.count - 1); - return result; - } - - if ((b0 & 0b1110'0000) == 0b1100'0000) { - if (stream.count < 2) - return result; - if ((b1 & 0b1100'0000) != 0b1000'0000) - return result; - DN_U32 cp = ((b0 & 0b0001'1111) << 6) | ((b1 & 0b0011'1111) << 0); - if (cp < 0x80) - return result; - result.codepoint = cp; - result.success = true; - result.remaining = DN_Str8FromPtr(stream.data + 2, stream.count - 2); - return result; - } - - if ((b0 & 0b1111'0000) == 0b1110'0000) { - if (stream.count < 3) - return result; - if ((b1 & 0b1100'0000) != 0b1000'0000) - return result; - if ((b2 & 0b1100'0000) != 0b1000'0000) - return result; - DN_U32 cp = ((b0 & 0b0000'1111) << 12) | ((b1 & 0b0011'1111) << 6) | ((b2 & 0b0011'1111) << 0); - if (cp < 0x800) - return result; - result.codepoint = cp; - result.success = true; - result.remaining = DN_Str8FromPtr(stream.data + 3, stream.count - 3); - return result; - } - - if ((b0 & 0b1111'1000) == 0b1111'0000) { - if (stream.count < 4) - return result; - if ((b1 & 0b1100'0000) != 0b1000'0000) - return result; - if ((b2 & 0b1100'0000) != 0b1000'0000) - return result; - if ((b3 & 0b1100'0000) != 0b1000'0000) - return result; - DN_U32 cp = ((b0 & 0b0000'0111) << 18) | - ((b1 & 0b0011'1111) << 12) | - ((b2 & 0b0011'1111) << 6) | - ((b3 & 0b0011'1111) << 0); - if (cp < 0x10000 || cp > 0x10FFFF) - return result; - result.codepoint = cp; - result.success = true; - result.remaining = DN_Str8FromPtr(stream.data + 4, stream.count - 4); - return result; - } - - return result; -} - -DN_API bool DN_UTF8DecodeIterate(DN_UTF8DecodeIterator *it, DN_Str8 utf8) -{ - if (it->init) { - it->codepoint_index++; - } else { - it->remaining = utf8; - it->init = true; - } - DN_UTF8DecodeResult decode = DN_UTF8Decode(it->remaining); - it->success = decode.success; - it->remaining = decode.remaining; - it->codepoint = decode.codepoint; - bool result = it->success; - return result; -} - -DN_API int DN_UTF16Encode(DN_U16 utf16[2], DN_U32 codepoint) +DN_API DN_USize DN_Utf32FromUtf16(DN_U16 utf16[2], DN_U32 codepoint) { // NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/ // ----------------------------------------+------------------------------------+------------------+ @@ -5254,6 +5212,130 @@ DN_API int DN_UTF16Encode(DN_U16 utf16[2], DN_U32 codepoint) return 0; } +DN_API DN_Utf32FromResult DN_Utf32FromUtf8Stream(DN_Str8 stream) +{ + DN_Utf32FromResult result = {}; + result.remaining = stream; + if (stream.count <= 0) + return result; + + // NOTE: Decompose stream into 4 potential utf8 bytes + DN_U8 b0 = DN_Cast(DN_U8)stream.data[0]; + DN_U8 b1 = DN_Cast(DN_U8)(stream.count >= 2 ? stream.data[1] : 0); + DN_U8 b2 = DN_Cast(DN_U8)(stream.count >= 3 ? stream.data[2] : 0); + DN_U8 b3 = DN_Cast(DN_U8)(stream.count >= 4 ? stream.data[3] : 0); + + // NOTE: Decode + if ((b0 & 0b1000'0000) == 0) { + result.codepoint = b0; + result.success = true; + result.remaining = DN_Str8FromPtr(stream.data + 1, stream.count - 1); + result.byte_count = 1; + return result; + } + + if ((b0 & 0b1110'0000) == 0b1100'0000) { + if (stream.count < 2) + return result; + if ((b1 & 0b1100'0000) != 0b1000'0000) + return result; + DN_U32 cp = ((b0 & 0b0001'1111) << 6) | ((b1 & 0b0011'1111) << 0); + if (cp < 0x80) + return result; + result.codepoint = cp; + result.success = true; + result.remaining = DN_Str8FromPtr(stream.data + 2, stream.count - 2); + result.byte_count = 2; + return result; + } + + if ((b0 & 0b1111'0000) == 0b1110'0000) { + if (stream.count < 3) + return result; + if ((b1 & 0b1100'0000) != 0b1000'0000) + return result; + if ((b2 & 0b1100'0000) != 0b1000'0000) + return result; + DN_U32 cp = ((b0 & 0b0000'1111) << 12) | ((b1 & 0b0011'1111) << 6) | ((b2 & 0b0011'1111) << 0); + if (cp < 0x800) + return result; + result.codepoint = cp; + result.success = true; + result.remaining = DN_Str8FromPtr(stream.data + 3, stream.count - 3); + result.byte_count = 3; + return result; + } + + if ((b0 & 0b1111'1000) == 0b1111'0000) { + if (stream.count < 4) + return result; + if ((b1 & 0b1100'0000) != 0b1000'0000) + return result; + if ((b2 & 0b1100'0000) != 0b1000'0000) + return result; + if ((b3 & 0b1100'0000) != 0b1000'0000) + return result; + DN_U32 cp = ((b0 & 0b0000'0111) << 18) | ((b1 & 0b0011'1111) << 12) | ((b2 & 0b0011'1111) << 6) | ((b3 & 0b0011'1111) << 0); + if (cp < 0x10000 || cp > 0x10FFFF) + return result; + result.codepoint = cp; + result.success = true; + result.remaining = DN_Str8FromPtr(stream.data + 4, stream.count - 4); + result.byte_count = 4; + return result; + } + + return result; +} + +DN_API bool DN_Utf32FromUtf8Iterate(DN_Utf32FromUtf8Iterator *it, DN_Utf32IterateDir dir, DN_Str8 utf8) +{ + // NOTE: Iterate forwards + if (dir == DN_Utf32IterateDir_Forwards) { + if (it->init) { + it->codepoint_index++; + } else { + it->remaining = utf8; + it->init = true; + } + DN_Utf32FromResult decode = DN_Utf32FromUtf8Stream(it->remaining); + it->success = decode.success; + it->remaining = decode.remaining; + it->codepoint = decode.codepoint; + it->bytes_decoded_ = decode.byte_count; + it->bytes_decoded_total += decode.byte_count; + } + + // NOTE: Or otherwise iterate backwards + if (dir == DN_Utf32IterateDir_Reverse) { + bool first_time = it->init == false; + if (first_time) { + it->remaining = utf8; + it->init = true; + } + + it->success = false; + for (DN_USize codepoint_size = 1; codepoint_size <= 4; codepoint_size++) { + DN_Str8 utf8_stream = DN_Str8Subset(it->remaining, it->remaining.count - codepoint_size, codepoint_size); + DN_Utf32FromResult utf32_from = DN_Utf32FromUtf8Stream(utf8_stream); + if (utf32_from.success) { + DN_Assert(utf32_from.byte_count <= it->remaining.count); + it->success = true; + it->remaining = DN_Str8FromPtr(it->remaining.data, it->remaining.count - utf32_from.byte_count); + it->bytes_decoded_ = utf32_from.byte_count; + it->bytes_decoded_total += utf32_from.byte_count; + it->codepoint = utf32_from.codepoint; + break; + } + } + + if (!first_time && it->success) + it->codepoint_index++; + } + + bool result = it->success; + return result; +} DN_API DN_U8 DN_U8FromHexNibble(char hex) { @@ -5721,8 +5803,8 @@ DN_API DN_Profiler DN_ProfilerInit(DN_ProfilerAnchor *anchors, DN_USize count, D "frequency of rdtsc yourself. The reason for this is that measuring rdtsc requires " "having some alternate timing mechanism to measure the duration between the Tscs " "provided by rdtsc and this profiler makes no assumption about what timing primitives " - "are available other than rdtsc which is a CPU builtin available on basically all " - "platforms or have an equivalent (e.g. __builtin_readcyclecounter)" + "that are available other than rdtsc which is a CPU builtin available on basically " + "all platforms or have an equivalent (e.g. __builtin_readcyclecounter)" "" "This codebase provides DN_OS_EstimateTscPerSecond() as an example of how to that for " "convenience and is available if compiling with the OS layer. Some platforms like " @@ -5891,6 +5973,291 @@ DN_API DN_F64 DN_ProfilerMsFromTsc(DN_Profiler *profiler, DN_U64 duration_tsc) return result; } +DN_API DN_Profiler2 DN_Profiler2Init(DN_ProfilerTscNowFunc *tsc_now_func, DN_U64 tsc_frequency, DN_Profiler2Marker *markers, DN_U32 markers_count, DN_Profiler2Frame *frames, DN_U32 frames_count) +{ + DN_Profiler2 result = {}; + result.tsc_now = tsc_now_func; + result.tsc_frequency = tsc_frequency; + result.frames = frames; + result.frames_max = frames_count; + result.markers = markers; + result.markers_max = markers_count; + return result; +} + +DN_API void DN_Profiler2FrameBegin(DN_Profiler2 *profiler) +{ + profiler->frame_num++; + + if (profiler->frames_max == 0) + return; + if (profiler->markers_max == 0) + return; + + // NOTE: Mid-frame someone paused the profiler. We will discard this frame's profiling data + // because the frame timestamp counter is going to account for the pause time which is going to + // spike the results unnecessarily. + if (profiler->state == DN_Profiler2State_UnpauseEnqueued) { + // NOTE: Discard the frame's data + profiler->active_frame = nullptr; + profiler->frame_begin_zone = {}; + if (profiler->frames_write_index_unwrapped) + profiler->frames_write_index_unwrapped -= 1; // Allow re-use of the frame + + // NOTE: Eat the unpause state, and transition into running state + profiler->state = DN_Profiler2State_Run; + } + + if (profiler->state != DN_Profiler2State_Run) + return; + + // NOTE: Reset the active parent + profiler->active_parent_index = 0; + + // NOTE: Calling frame-begin itself starts a profiling zone that encompasses the elapsed time + // between frame-begin calls (in an interactive application this would be the duration of + // essentially update loop). First we end the prior zone if it exists. + DN_U64 prev_frame_zone_tsc_end = 0; + if (profiler->frame_begin_zone.marker) { + prev_frame_zone_tsc_end = profiler->frame_begin_zone.marker->tsc_end; + DN_Profiler2ZoneEnd(&profiler->frame_begin_zone); + } + + // NOTE: Grab a copy of the previous frame + DN_Profiler2Frame prev_frame = {}; + if (profiler->frames_write_index_unwrapped > 0) { + DN_USize prev_frame_index = (profiler->frames_write_index_unwrapped - 1) % profiler->frames_max; + prev_frame = profiler->frames[prev_frame_index]; + } + + // NOTE: Grab a copy of the oldest frame (essentially the frame we're going to write into if + // we've wrapped, otherwise no-op here). We copy it out for clarity of semantics because we're + // about to write to the oldest frame in the scenario that we've already wrapped all the frames. + DN_USize frames_write_index = profiler->frames_write_index_unwrapped % profiler->frames_max; + DN_Profiler2Frame oldest_frame = {}; + if (profiler->frames_write_index_unwrapped >= profiler->frames_max) + oldest_frame = profiler->frames[frames_write_index]; + + // NOTE: Calculate the range into `profiler->markers` that the previous frame used + DN_V2USize prev_offset = {}; + if (profiler->frames_write_index_unwrapped > 0) + prev_offset = DN_Profiler2MarkerOffsetFromFrame(profiler, &prev_frame); + + DN_Profiler2Frame *frame = profiler->frames + frames_write_index; + if (profiler->frames_write_index_unwrapped < profiler->frames_max) { + // NOTE: If we haven't wrapped frames yet, the maximum allowed markers is exactly the + // remaining markers that haven't been used in the markers array because there's no frames to + // "reclaim" markers from yet. + frame->max = profiler->markers_max - DN_Cast(DN_U32)prev_offset.end; + } else { + // NOTE: Suppose markers_max = 100, frames_max = 3 + // + // Frame 0: begin=0, end=10 (oldest, being evicted) + // Frame 1: begin=10, end=25 (live) + // Frame 2: begin=25, end=40 (prev, live) + // + // On frame 3 we are going to overwrite frame 0. The range of markers we can write becomes + // markers [40, 100] and [0, 10]. We can calculate this delta as being + // `oldest.end - prev.end`. This calculation works if `oldest.end > prev.end` but we can make + // it work universally by adding `markers_max` to ensure it's positive for the modulo. + // + // The biggest delta that `oldest.end - prev.end` can produce is exactly `markers_max` and the + // modulo wraps everything back into the proper range. + DN_V2USize oldest_offset = DN_Profiler2MarkerOffsetFromFrame(profiler, &oldest_frame); + DN_U32 delta = DN_Cast(DN_U32)(oldest_offset.end - prev_offset.end); + frame->max = (delta + profiler->markers_max) % profiler->markers_max; + } + + frame->begin_offset = DN_Cast(DN_U32)prev_offset.end; + frame->count = DN_Min(1, frame->max); // Reserve the 0th marker for the nil-marker if possible + + // NOTE: Zero out the markers for this frame, do this upfront so that we don't memset inside the + // zone. The markers ring buffer may wrap around the end of the global markers array so we may + // need two memsets. + if (frame->begin_offset + frame->max > profiler->markers_max) { + DN_USize first_count = profiler->markers_max - frame->begin_offset; + DN_USize second_count = frame->max - first_count; + DN_Memset(profiler->markers + frame->begin_offset, 0, sizeof(DN_Profiler2Marker) * first_count); + DN_Memset(profiler->markers, 0, sizeof(DN_Profiler2Marker) * second_count); + } else { + DN_Memset(profiler->markers + frame->begin_offset, 0, sizeof(DN_Profiler2Marker) * frame->max); + } + + profiler->active_frame = frame; + profiler->active_frame->frame_num = profiler->frame_num - 1; + profiler->frames_write_index_unwrapped++; + + // NOTE: Start a new profiling zone once the frame has been setup. We do a small trick and set + // the begin TSC to the previous frame (if it exists) so that we capture all the time that has + // elapsed since ending the previous frame. We can't immediately update the zone because we need + // to setup the frame/markers. + profiler->frame_begin_zone = DN_Profiler2ZoneBeginName(profiler, "Profiler Frame"); + profiler->active_frame->begin_marker = profiler->frame_begin_zone.marker; + if (prev_frame_zone_tsc_end) + profiler->active_frame->begin_marker->tsc_begin = prev_frame_zone_tsc_end; +} + +static bool DN_Profiler2FunctionTimingLessThan_(void const *lhs, void const *rhs, void *) +{ + DN_Profiler2FunctionTime const *lhs_timing = DN_Cast(DN_Profiler2FunctionTime const *)lhs; + DN_Profiler2FunctionTime const *rhs_timing = DN_Cast(DN_Profiler2FunctionTime const *)rhs; + DN_Str8 lhs_file = lhs_timing->call_site.file; + DN_Str8 rhs_file = rhs_timing->call_site.file; + bool result = false; + int cmp = DN_Memcmp(lhs_file.data, rhs_file.data, DN_Min(lhs_file.count, rhs_file.count)); + if (cmp == 0 && lhs_file.count == rhs_file.count) + result = lhs_timing->call_site.line < rhs_timing->call_site.line; + else if (cmp == 0) + result = lhs_file.count < rhs_file.count; + else + result = cmp < 0; + return result; +} + +DN_API DN_Profiler2FunctionTimes DN_Profiler2FunctionTimesFromFrame(DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame, DN_Arena *arena) +{ + DN_Profiler2FunctionTimes result = {}; + if (!frame) + return result; + + DN_PArrayPrepareArena(result.data, result.count, &result.max, arena, 128); + for (DN_ForIndexU(index, frame->count)) { + if (index == 0) + continue; + DN_Profiler2Marker* marker = DN_Profiler2MarkerFromFrameIndex(DN_Cast(DN_Profiler2 *)profiler, DN_Cast(DN_Profiler2Frame *)frame, DN_Cast(DN_U32)index); + DN_Profiler2FunctionTime find = {}; + find.call_site = marker->call_site; + + DN_Profiler2FunctionTime *timing = nullptr; + DN_BSearchResult search = DN_BSearch(result.data, result.count, sizeof(*result.data), &find, DN_BSearchType_Match, nullptr, DN_Profiler2FunctionTimingLessThan_); + if (search.found) + timing = result.data + search.index; + else + timing = DN_PArrayInsert(result.data, &result.count, result.max, search.index, find); + + timing->name = marker->name; + timing->hit_count++; + timing->tsc_total += marker->tsc_end - marker->tsc_begin; + timing->tsc_children_total += marker->tsc_children_duration; + } + return result; +} + +DN_API DN_Profiler2Zone DN_Profiler2ZoneBegin(DN_Profiler2 *profiler, DN_Str8 name, DN_CallSite call_site) +{ + DN_Profiler2Zone result = {}; + DN_Profiler2Frame *frame = profiler && profiler->state == DN_Profiler2State_Run ? profiler->active_frame : nullptr; + if (frame) { + if (frame->count < frame->max) { + result.marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, frame->count); + frame->count++; + } + } + + if (result.marker) { + DN_U32 marker_index = frame->count - 1; + DN_Profiler2Marker *marker = result.marker; + marker->call_site = call_site; + marker->tsc_begin = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTsc(); + marker->parent_index = profiler->active_parent_index; + marker->name = name; + + // NOTE: Attach the zone to the parent + DN_Profiler2Marker *parent = DN_Profiler2MarkerFromFrameIndex(profiler, frame, marker->parent_index); + if (parent->first_child_index == 0) { + parent->first_child_index = marker_index; + DN_Assert(parent->last_child_index == 0); + } + + // NOTE: Unconditionally update the parent's last child, always valid even if last child points to + // the zeroth item since that's the nil-item, we can write garbage because nothing's assigned to + // it. + DN_Profiler2Marker *last_child = DN_Profiler2MarkerFromFrameIndex(profiler, frame, parent->last_child_index); + last_child->next_index = marker_index; + parent->last_child_index = marker_index; + + // NOTE: Set this zone as the active zone that nested zone should attach to + profiler->active_parent_index = marker_index; + result.profiler = profiler; + } + return result; +} + +DN_API void DN_Profiler2ZoneEnd(DN_Profiler2Zone *zone) +{ + zone->scope_once = true; + if (!zone->marker) + return; + + DN_Assert(!zone->ended); + zone->ended = true; + + // NOTE: Record the end timestamp + DN_Profiler2* profiler = zone->profiler; + zone->marker->tsc_end = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTsc(); + + // NOTE: Calculate the total time consumed into the parent + if (profiler->frame_begin_zone.marker != zone->marker) { + DN_Profiler2Marker* parent = DN_Profiler2MarkerFromFrameIndex(profiler, profiler->active_frame, zone->marker->parent_index); + parent->tsc_children_duration += zone->marker->tsc_end - zone->marker->tsc_begin; + } + + // NOTE: Pop the active parent from the profiler + profiler->active_parent_index = zone->marker->parent_index; +} + +DN_API bool DN_Profiler2MarkerWalkDfsPreOrder(DN_Profiler2 *profiler, DN_Profiler2Iterator *it) +{ + if (!profiler || !it || profiler->active_frame->count <= 1) + return false; + + DN_Profiler2Frame *frame = profiler->active_frame; + if (!it->init) { + it->init = true; + it->marker_index = 1; + it->iteration_count = 1; + it->last_visited_index = 0; + it->marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, 1); + return true; + } + + it->last_visited_index = it->marker_index; + it->iteration_count++; + + DN_Profiler2Marker *marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + if (marker->first_child_index != 0) { + it->marker_index = marker->first_child_index; + } else { + for (;;) { + if (marker->next_index != 0) { + it->marker_index = marker->next_index; + break; + } + if (marker->parent_index == 0) + return false; + it->marker_index = marker->parent_index; + marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + } + } + + it->marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + return true; +} + +DN_API DN_V2USize DN_Profiler2MarkerOffsetFromFrame(DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame) +{ + DN_V2USize result = {}; + result.begin = frame->begin_offset; + result.end = (result.begin + frame->count) % profiler->markers_max; + return result; +} + +DN_API DN_Profiler2Marker *DN_Profiler2MarkerFromFrameIndex(DN_Profiler2 *profiler, DN_Profiler2Frame *frame, DN_U32 index) +{ + DN_Profiler2Marker *result = profiler->markers + ((frame->begin_offset + index) % profiler->markers_max); + return result; +} + static void DN_QSortSetElem_(void *array, DN_USize elem_size, DN_USize dest_index, DN_USize src_index) { char *src = DN_Cast(char *) array + (src_index * elem_size); @@ -6258,230 +6625,310 @@ DN_API DN_U64 DN_Fnv1aHashU64FromBytes(void const *bytes, DN_USize count, DN_U64 return hash; } -#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL) - #define DN_MMH3_ROTL32(x, y) _rotl(x, y) - #define DN_MMH3_ROTL64(x, y) _rotl64(x, y) -#else - #define DN_MMH3_ROTL32(x, y) ((x) << (y)) | ((x) >> (32 - (y))) - #define DN_MMH3_ROTL64(x, y) ((x) << (y)) | ((x) >> (64 - (y))) +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/*----------------------------------------------------------------------------- +If you want to understand the MurmurHash algorithm you would be much better +off reading the original source. Just point your browser at: +http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + + +What this version provides? + +1. Progressive data feeding. Useful when the entire payload to be hashed +does not fit in memory or when the data is streamed through the application. +Also useful when hashing a number of strings with a common prefix. A partial +hash of a prefix string can be generated and reused for each suffix string. + +2. Portability. Plain old C so that it should compile on any old compiler. +Both CPU endian and access-alignment neutral, but avoiding inefficient code +when possible depending on CPU capabilities. + +3. Drop in. I personally like nice self contained public domain code, making it +easy to pilfer without loads of refactoring to work properly in the existing +application code & makefile structure and mucking around with licence files. +Just copy PMurHash.h and PMurHash.c and you're ready to go. + + +How does it work? + +We can only process entire 32 bit chunks of input, except for the very end +that may be shorter. So along with the partial hash we need to give back to +the caller a carry containing up to 3 bytes that we were unable to process. +This carry also needs to record the number of bytes the carry holds. I use +the low 2 bits as a count (0..3) and the carry bytes are shifted into the +high byte in stream order. + +To handle endianess I simply use a macro that reads a uint32_t and define +that macro to be a direct read on little endian machines, a read and swap +on big endian machines, or a byte-by-byte read if the endianess is unknown. + +-----------------------------------------------------------------------------*/ +/* MSVC warnings we choose to ignore */ +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4127) /* conditional expression is constant */ #endif -//----------------------------------------------------------------------------- -// Block read - if your platform needs to do endian-swapping or can only -// handle aligned reads, do the conversion here -DN_FORCE_INLINE DN_U32 DN_Murmur3GetBlock32_(DN_U32 const *p, int i) +/*----------------------------------------------------------------------------- + * Endianess, misalignment capabilities and util macros + * + * The following 3 macros are defined in this section. The other macros defined + * are only needed to help derive these 3. + * + * DN_M3H_READ_UINT32(x) Read a little endian unsigned 32-bit int + * DN_M3H_UNALIGNED_SAFE Defined if DN_M3H_READ_UINT32 works on non-word boundaries + * DN_M3H_ROTL32(x,r) Rotate x left by r bits + */ + +/* Convention is to define __BYTE_ORDER == to one of these values */ +#if !defined(DN_M3H__BIG_ENDIAN) + #define DN_M3H__BIG_ENDIAN 4321 +#endif +#if !defined(DN_M3H__LITTLE_ENDIAN) + #define DN_M3H__LITTLE_ENDIAN 1234 +#endif + +/* I386 */ +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386) + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #define DN_M3H_UNALIGNED_SAFE +#endif + +/* gcc 'may' define DN_M3H__LITTLE_ENDIAN__ or DN_M3H__BIG_ENDIAN__ to 1 (Note the trailing __), + * or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */ +#if !defined(DN_M3H__BYTE_ORDER) + #if defined(DN_M3H__LITTLE_ENDIAN__) && DN_M3H__LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1 + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #elif defined(DN_M3H__BIG_ENDIAN__) && DN_M3H__BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1 + #define DN_M3H__BYTE_ORDER DN_M3H__BIG_ENDIAN + #endif +#endif + +/* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */ +#if !defined(DN_M3H__BYTE_ORDER) + #if defined(__ARMEL__) || defined(__MIPSEL__) + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #endif + #if defined(__ARMEB__) || defined(__MIPSEB__) + #define DN_M3H__BYTE_ORDER DN_M3H__BIG_ENDIAN + #endif +#endif + +/* Now find best way we can to DN_M3H_READ_UINT32 */ +#if DN_M3H__BYTE_ORDER==DN_M3H__LITTLE_ENDIAN + /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ + #define DN_M3H_READ_UINT32(ptr) (*((DN_U32*)(ptr))) +#elif DN_M3H__BYTE_ORDER==DN_M3H__BIG_ENDIAN + /* TODO: Add additional cases below where a compiler provided bswap32 is available */ + #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) + #define DN_M3H_READ_UINT32(ptr) (__builtin_bswap32(*((DN_U32*)(ptr)))) + #else + /* Without a known fast bswap32 we're just as well off doing this */ + #define DN_M3H_READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + #define DN_M3H_UNALIGNED_SAFE + #endif +#else + /* Unknown endianess so last resort is to read individual bytes */ + #define DN_M3H_READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + + /* Since we're not doing word-reads we can skip the messing about with realignment */ + #define DN_M3H_UNALIGNED_SAFE +#endif + +/* Find best way to ROTL32 */ +#if defined(_MSC_VER) + #include /* Microsoft put _rotl declaration in here */ + #define DN_M3H_ROTL32(x,r) _rotl(x,r) +#else + /* gcc recognises this code and generates a rotate instruction for CPUs with one */ + #define DN_M3H_ROTL32(x,r) (((DN_U32)x << r) | ((DN_U32)x >> (32 - r))) +#endif + + +/*----------------------------------------------------------------------------- + * Core murmurhash algorithm macros */ + +#define DN_M3H_C1 (0xcc9e2d51) +#define DN_M3H_C2 (0x1b873593) + +/* This is the main processing body of the algorithm. It operates + * on each full 32-bits of input. */ +#define DN_M3H_DOBLOCK(h1, k1) do{ \ + k1 *= DN_M3H_C1; \ + k1 = DN_M3H_ROTL32(k1,15); \ + k1 *= DN_M3H_C2; \ + \ + h1 ^= k1; \ + h1 = DN_M3H_ROTL32(h1,13); \ + h1 = h1*5+0xe6546b64; \ + }while(0) + +/* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ +/* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ +#define DN_M3H_DOBYTES(cnt, h1, c, n, ptr, len) do{ \ + int _i = cnt; \ + while(_i--) { \ + c = c>>8 | *ptr++<<24; \ + n++; len--; \ + if(n==4) { \ + DN_M3H_DOBLOCK(h1, c); \ + n = 0; \ + } \ + } }while(0) + +/*---------------------------------------------------------------------------*/ + +/* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed + * if wanted. Both ph1 and pcarry are required arguments. */ +DN_API void DN_Murmur3HashRawProcess(DN_U32 *ph1, DN_U32 *pcarry, const void *key, int len) { - return p[i]; + DN_U32 h1 = *ph1; + DN_U32 c = *pcarry; + + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end; + + /* Extract carry count from low 2 bits of c value */ + int n = c & 3; + +#if defined(DN_M3H_UNALIGNED_SAFE) + /* This CPU handles unaligned word access */ + + /* Consume any carry bytes */ + int i = (4-n) & 3; + if(i && i <= len) { + DN_M3H_DOBYTES(i, h1, c, n, ptr, len); + } + + /* Process 32-bit chunks */ + end = ptr + len/4*4; + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = DN_M3H_READ_UINT32(ptr); + DN_M3H_DOBLOCK(h1, k1); + } + +#else /*DN_M3H_UNALIGNED_SAFE*/ + /* This CPU does not handle unaligned word access */ + + /* Consume enough so that the next data byte is word aligned */ + int i = -(long)ptr & 3; + if(i && i <= len) { + DN_M3H_DOBYTES(i, h1, c, n, ptr, len); + } + + /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ + end = ptr + len/4*4; + switch(n) { /* how many bytes in c */ + case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = DN_M3H_READ_UINT32(ptr); + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>24; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<8; + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>16; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<16; + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>8; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<24; + DN_M3H_DOBLOCK(h1, k1); + } + } +#endif /*DN_M3H_UNALIGNED_SAFE*/ + + /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ + len -= len/4*4; + + /* Append any remaining bytes into carry */ + DN_M3H_DOBYTES(len, h1, c, n, ptr, len); + + /* Copy out new running hash and carry */ + *ph1 = h1; + *pcarry = (c & ~0xff) | n; } -DN_FORCE_INLINE DN_U64 DN_Murmur3GetBlock64_(DN_U64 const *p, int i) -{ - return p[i]; -} +/*---------------------------------------------------------------------------*/ -//----------------------------------------------------------------------------- -// Finalization mix - force all bits of a hash block to avalanche - -DN_FORCE_INLINE DN_U32 DN_Murmur3FMix32_(DN_U32 h) +/* Finalize a hash. To match the original Murmur3A the total_length must be provided */ +DN_API DN_U32 DN_Murmur3HashRawResult(DN_U32 h, DN_U32 carry, DN_U32 total_length) { + DN_U32 k1; + int n = carry & 3; + if(n) { + k1 = carry >> (4-n)*8; + k1 *= DN_M3H_C1; k1 = DN_M3H_ROTL32(k1,15); k1 *= DN_M3H_C2; h ^= k1; + } + h ^= total_length; + + /* fmix */ h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; + return h; } -DN_FORCE_INLINE DN_U64 DN_Murmur3FMix64_(DN_U64 k) +/*---------------------------------------------------------------------------*/ + +/* Murmur3A compatable all-at-once */ +DN_API DN_U32 DN_Murmur3Hash(DN_U32 seed, const void *key, int len) { - k ^= k >> 33; - k *= 0xff51afd7ed558ccd; - k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53; - k ^= k >> 33; - return k; + DN_U32 h1=seed, carry=0; + DN_Murmur3HashRawProcess(&h1, &carry, key, len); + DN_U32 result = DN_Murmur3HashRawResult(h1, carry, len); + return result; } -DN_API DN_U32 DN_Murmur3HashU32FromBytesX86(void const *bytes, int len, DN_U32 seed) +DN_API DN_Murmur3Context DN_Murmur3HashInit(DN_U32 seed) { - const DN_U8 *data = (const DN_U8 *)bytes; - const int nblocks = len / 4; + DN_Murmur3Context result = {}; + result.it = seed; + return result; +} - DN_U32 h1 = seed; - - const DN_U32 c1 = 0xcc9e2d51; - const DN_U32 c2 = 0x1b873593; - - //---------- - // body - - const DN_U32 *blocks = (const DN_U32 *)(data + nblocks * 4); - - for (int i = -nblocks; i; i++) { - DN_U32 k1 = DN_Murmur3GetBlock32_(blocks, i); - - k1 *= c1; - k1 = DN_MMH3_ROTL32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = DN_MMH3_ROTL32(h1, 13); - h1 = h1 * 5 + 0xe6546b64; +DN_API void DN_Murmur3HashUpdate(DN_Murmur3Context *ctx, const void *key, int len) +{ + if (len > 0) { + DN_Murmur3HashRawProcess(&ctx->it, &ctx->carry, key, len); + ctx->bytes_count += len; } - - //---------- - // tail - - const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 4); - - DN_U32 k1 = 0; - - switch (len & 3) { - case 3: - k1 ^= tail[2] << 16; - case 2: - k1 ^= tail[1] << 8; - case 1: - k1 ^= tail[0]; - k1 *= c1; - k1 = DN_MMH3_ROTL32(k1, 15); - k1 *= c2; - h1 ^= k1; - }; - - //---------- - // finalization - - h1 ^= len; - - h1 = DN_Murmur3FMix32_(h1); - - return h1; } -DN_API DN_Murmur3 DN_Murmur3HashU128FromBytesX64(void const *bytes, int len, DN_U32 seed) +DN_API DN_U32 DN_Murmur3HashFinish(DN_Murmur3Context const *ctx) { - const DN_U8 *data = (const DN_U8 *)bytes; - const int nblocks = len / 16; - - DN_U64 h1 = seed; - DN_U64 h2 = seed; - - const DN_U64 c1 = 0x87c37b91114253d5; - const DN_U64 c2 = 0x4cf5ad432745937f; - - //---------- - // body - - const DN_U64 *blocks = (const DN_U64 *)(data); - - for (int i = 0; i < nblocks; i++) { - DN_U64 k1 = DN_Murmur3GetBlock64_(blocks, i * 2 + 0); - DN_U64 k2 = DN_Murmur3GetBlock64_(blocks, i * 2 + 1); - - k1 *= c1; - k1 = DN_MMH3_ROTL64(k1, 31); - k1 *= c2; - h1 ^= k1; - - h1 = DN_MMH3_ROTL64(h1, 27); - h1 += h2; - h1 = h1 * 5 + 0x52dce729; - - k2 *= c2; - k2 = DN_MMH3_ROTL64(k2, 33); - k2 *= c1; - h2 ^= k2; - - h2 = DN_MMH3_ROTL64(h2, 31); - h2 += h1; - h2 = h2 * 5 + 0x38495ab5; - } - - //---------- - // tail - - const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 16); - - DN_U64 k1 = 0; - DN_U64 k2 = 0; - - switch (len & 15) { - case 15: - k2 ^= ((DN_U64)tail[14]) << 48; - case 14: - k2 ^= ((DN_U64)tail[13]) << 40; - case 13: - k2 ^= ((DN_U64)tail[12]) << 32; - case 12: - k2 ^= ((DN_U64)tail[11]) << 24; - case 11: - k2 ^= ((DN_U64)tail[10]) << 16; - case 10: - k2 ^= ((DN_U64)tail[9]) << 8; - case 9: - k2 ^= ((DN_U64)tail[8]) << 0; - k2 *= c2; - k2 = DN_MMH3_ROTL64(k2, 33); - k2 *= c1; - h2 ^= k2; - - case 8: - k1 ^= ((DN_U64)tail[7]) << 56; - case 7: - k1 ^= ((DN_U64)tail[6]) << 48; - case 6: - k1 ^= ((DN_U64)tail[5]) << 40; - case 5: - k1 ^= ((DN_U64)tail[4]) << 32; - case 4: - k1 ^= ((DN_U64)tail[3]) << 24; - case 3: - k1 ^= ((DN_U64)tail[2]) << 16; - case 2: - k1 ^= ((DN_U64)tail[1]) << 8; - case 1: - k1 ^= ((DN_U64)tail[0]) << 0; - k1 *= c1; - k1 = DN_MMH3_ROTL64(k1, 31); - k1 *= c2; - h1 ^= k1; - }; - - //---------- - // finalization - - h1 ^= len; - h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = DN_Murmur3FMix64_(h1); - h2 = DN_Murmur3FMix64_(h2); - - h1 += h2; - h2 += h1; - - DN_Murmur3 result = {}; - result.e[0] = h1; - result.e[1] = h2; + DN_U32 result = DN_Murmur3HashRawResult(ctx->it, ctx->carry, ctx->bytes_count); return result; } -DN_API DN_U64 DN_Murmur3HashU64FromBytesX64(void const *bytes, int len, DN_U32 seed) -{ - DN_Murmur3 hash = DN_Murmur3HashU128FromBytesX64(bytes, len, seed); - DN_U64 result = hash.e[0]; - return result; -} - -DN_API DN_U32 DN_Murmur3HashU32FromBytesX64(void const *bytes, int len, DN_U32 seed) -{ - DN_Murmur3 hash = DN_Murmur3HashU128FromBytesX64(bytes, len, seed); - DN_U32 result = DN_Cast(DN_U32)hash.e[0]; - return result; -} +#if defined(_MSC_VER) + #pragma warning(pop) +#endif DN_API DN_Str8x32 DN_Str8x32FromAnsiColourCodeU8Rgb(DN_AnsiColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b) { @@ -7548,36 +7995,36 @@ DN_API bool DN_V4F32Rgba01IsValid(DN_V4F32 rgba01) return result; } -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbU32(DN_U32 u32) +DN_API DN_V4F32 DN_V4F32FromRgbU32ToRgba01(DN_U32 rgb) { - DN_U8 r = (DN_U8)((u32 & 0x00FF0000) >> 16); - DN_U8 g = (DN_U8)((u32 & 0x0000FF00) >> 8); - DN_U8 b = (DN_U8)((u32 & 0x000000FF) >> 0); - DN_V4F32 result = DN_V4F32Rgba01FromRgbU8(r, g, b); + DN_U8 r = (DN_U8)((rgb & 0x00FF0000) >> 16); + DN_U8 g = (DN_U8)((rgb & 0x0000FF00) >> 8); + DN_U8 b = (DN_U8)((rgb & 0x000000FF) >> 0); + DN_V4F32 result = DN_V4F32FromRgb255ToRgba01(r, g, b); return result; } -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbaU32(DN_U32 u32) +DN_API DN_V4F32 DN_V4F32FromRgbaU32ToRgba01(DN_U32 u32) { DN_U8 r = (DN_U8)((u32 & 0xFF000000) >> 24); DN_U8 g = (DN_U8)((u32 & 0x00FF0000) >> 16); DN_U8 b = (DN_U8)((u32 & 0x0000FF00) >> 8); DN_U8 a = (DN_U8)((u32 & 0x000000FF) >> 0); - DN_V4F32 result = DN_V4F32Rgba01FromRgbaU8(r, g, b, a); + DN_V4F32 result = DN_V4F32FromRgba255ToRgba01(r, g, b, a); return result; } -#define DN_Srgb_COEFFICIENT_F32 2.2f -DN_API DN_V4F32 DN_V4F32Linear01FromSrgb01(DN_V4F32 srgb01) +#define DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32 2.2f +DN_API DN_V4F32 DN_V4F32FromSrgb01ToLinear01(DN_V4F32 srgb01) { DN_Assert(srgb01.x >= 0.f && srgb01.x <= 1.f); DN_Assert(srgb01.y >= 0.f && srgb01.y <= 1.f); DN_Assert(srgb01.z >= 0.f && srgb01.z <= 1.f); DN_Assert(srgb01.a >= 0.f && srgb01.a <= 1.f); DN_V4F32 result = {}; - result.r = DN_PowF32(srgb01.r, DN_Srgb_COEFFICIENT_F32); - result.g = DN_PowF32(srgb01.g, DN_Srgb_COEFFICIENT_F32); - result.b = DN_PowF32(srgb01.b, DN_Srgb_COEFFICIENT_F32); + result.r = DN_PowF32(srgb01.r, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.g = DN_PowF32(srgb01.g, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.b = DN_PowF32(srgb01.b, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); result.a = srgb01.a; return result; } @@ -7590,16 +8037,16 @@ DN_API DN_V4F32 DN_V4F32Linear01Desaturate(DN_V4F32 linear01, DN_F32 t01) return result; } -DN_API DN_V4F32 DN_V4F32Srgb01FromLinear01(DN_V4F32 linear01) +DN_API DN_V4F32 DN_V4F32FromLinear01ToSrgb01(DN_V4F32 linear01) { DN_Assert(linear01.x >= 0.f && linear01.x <= 1.f); DN_Assert(linear01.y >= 0.f && linear01.y <= 1.f); DN_Assert(linear01.z >= 0.f && linear01.z <= 1.f); DN_Assert(linear01.a >= 0.f && linear01.a <= 1.f); DN_V4F32 result = {}; - result.r = DN_PowF32(linear01.r, 1.f / DN_Srgb_COEFFICIENT_F32); - result.g = DN_PowF32(linear01.g, 1.f / DN_Srgb_COEFFICIENT_F32); - result.b = DN_PowF32(linear01.b, 1.f / DN_Srgb_COEFFICIENT_F32); + result.r = DN_PowF32(linear01.r, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.g = DN_PowF32(linear01.g, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.b = DN_PowF32(linear01.b, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); result.a = linear01.a; return result; } @@ -7993,6 +8440,13 @@ DN_API DN_M2x3 DN_M2x3Identity() return result; } +DN_API bool DN_M2x3IsIdentity(DN_M2x3 m2x3) +{ + DN_M2x3 identity = DN_M2x3Identity(); + bool result = DN_M2x3Eq(&m2x3, &identity); + return result; +} + DN_API DN_M2x3 DN_M2x3Translate(DN_V2F32 offset) { DN_M2x3 result = { @@ -8332,10 +8786,11 @@ DN_API DN_Rect DN_RectCutCut(DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip DN_Rect result = {}; if (rect_cut.rect) { switch (rect_cut.side) { - case DN_RectCutSide_Left: result = DN_RectCutLeftClip(rect_cut.rect, size.w, clip); break; - case DN_RectCutSide_Right: result = DN_RectCutRightClip(rect_cut.rect, size.w, clip); break; - case DN_RectCutSide_Top: result = DN_RectCutTopClip(rect_cut.rect, size.h, clip); break; - case DN_RectCutSide_Bottom: result = DN_RectCutBottomClip(rect_cut.rect, size.h, clip); break; + case DN_Side_Left: result = DN_RectCutLeftClip (rect_cut.rect, size.w, clip); break; + case DN_Side_Right: result = DN_RectCutRightClip (rect_cut.rect, size.w, clip); break; + case DN_Side_Top: result = DN_RectCutTopClip (rect_cut.rect, size.h, clip); break; + case DN_Side_Bottom: result = DN_RectCutBottomClip(rect_cut.rect, size.h, clip); break; + case DN_Side_Count: result = *rect_cut.rect; DN_AssertInvalidCodePath; break; } } return result; @@ -8595,13 +9050,14 @@ DN_API void *DN_ArrayMakeArrayPool(void **data, DN_USize *count, DN_USize *max, return result; } -DN_API void *DN_ArrayAddArray(void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +DN_API void *DN_ArrayAddArray(void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add) { void *result = DN_ArrayMakeArray(data, count, max, elem_size, elems_count, DN_ZMem_No); if (result) { - if (add == DN_ArrayAdd_Append) { + if (add == DN_AddType_Append) { DN_Memcpy(result, elems, elems_count * elem_size); } else { + DN_Assert(add == DN_AddType_Prepend); char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward char *move_src = DN_Cast(char *)data; DN_Memmove(move_dest, move_src, elem_size * count[0]); @@ -8611,7 +9067,7 @@ DN_API void *DN_ArrayAddArray(void *data, DN_USize *count, DN_USize max, DN_USiz return result; } -DN_API void *DN_ArrayAddArrayArena(void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +DN_API void *DN_ArrayAddArrayArena(void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, void const *elems, DN_USize elems_count, DN_AddType add) { void *result = nullptr; if (DN_ArrayPrepareArena(data, *count, max, elem_size, arena, elems_count)) @@ -8619,7 +9075,7 @@ DN_API void *DN_ArrayAddArrayArena(void **data, DN_USize *count, DN_USize *max, return result; } -DN_API void *DN_ArrayAddArrayPool(void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +DN_API void *DN_ArrayAddArrayPool(void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, void const *elems, DN_USize elems_count, DN_AddType add) { void *result = nullptr; if (DN_ArrayPreparePool(data, *count, max, elem_size, pool, elems_count)) @@ -8627,7 +9083,7 @@ DN_API void *DN_ArrayAddArrayPool(void **data, DN_USize *count, DN_USize *max, D return result; } -DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) +DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site) { void *result = DN_ArrayAddArray(data, count, max, elem_size, elems, elems_count, add); DN_AssertCallSiteF(result, call_site, "Array out of space, failed to add %zu items: array=%p size=%zu max=%zu", elems_count, data, *count, max); @@ -8743,16 +9199,6 @@ DN_API DN_USize DN_ArrayCopyPtrPoolAssert(void **data, void const *src, DN_USize return result; } -DN_API void *DN_SinglyLLDetach(void **link, void **next) -{ - void *result = *link; - if (*link) { - *link = *next; - *next = nullptr; - } - return result; -} - DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size) { DN_U64 avail = ring->write_pos - ring->read_pos; @@ -8800,7 +9246,7 @@ DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size) DN_API DN_U32 DN_HTableHashFuncMurmur3KeyBytes(void const *key, DN_USize size) { DN_U32 const DEFAULT_SEED = 0xb255b383; - DN_U32 result = DN_Murmur3HashU32FromBytes(key, DN_Cast(int)size, DEFAULT_SEED); + DN_U32 result = DN_Murmur3Hash(DEFAULT_SEED, key, DN_Cast(int)size); return result; } @@ -8992,10 +9438,11 @@ DN_API bool DN_HTableResize(DN_HTable *table, DN_USize new_max) void* new_kvs = {}; DN_USize new_kvs_size = new_max * table->size_of_kv; - if (table->flags & DN_HTableFlags_UsingHeap) - new_kvs = DN_HeapAlloc(&table->heap, /*reserve*/ new_kvs_size, /*commit*/ new_kvs_size); - else + if (table->flags & DN_HTableFlags_UsingHeap) { + new_kvs = DN_HeapAllocCallSite(&table->heap, /*reserve*/ new_kvs_size, /*commit*/ new_kvs_size, DN_HeapAllocFlag_Nil, DN_CallSiteNowNamed("DN HTable Resize")); + } else { new_kvs = DN_PoolAlloc(&table->pool, new_max * table->size_of_kv); + } if (new_kvs) { // NOTE: Copy over the old table slots into the new table storage @@ -10318,7 +10765,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) for (DN_TestScopeF(&result, "[Arena] Reused memory is zeroed out")) { uint8_t alignment = 1; DN_USize alloc_size = DN_Kilobytes(128); - DN_MemList mem = DN_MemListFromHeap(0, 0, DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(0, 0, DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Reused memory"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -10340,7 +10787,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) } for (DN_TestScopeF(&result, "[Arena] Grows naturally, 1mb + 4mb")) { - DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Grows naturally"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -10362,7 +10809,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) } for (DN_TestScopeF(&result, "[Arena] Grows naturally, 1mb, temp memory 4mb")) { - DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Grows naturally"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -11536,7 +11983,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) for (DN_ForItSize(entry_it, NETEntry, entries, entries_count)) { NETEntry *entry = entry_it.data; - DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "NET request"); DN_Str8 remote_ws_server_url = DN_Str8Lit("wss://echo.websocket.org"); DN_Str8 remote_http_server_url = DN_Str8Lit("https://google.com"); @@ -11609,28 +12056,88 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) DN_MSVC_WARNING_POP #endif // #if DN_WITH_TESTS -DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted) +DN_API DN_MemDebuggerSnapshot DN_MemDebuggerSnapshotMake(DN_MemDebugger *debugger, DN_Arena *arena) +{ + DN_MemDebuggerSnapshot result = {}; + if (!debugger) + return result; + + DN_TicketMutexBegin(&debugger->alloc_table_mutex); + { + DN_USize count_req = debugger->alloc_table.count; + + // NOTE: Exclude this allocation from the memory debugger otherwise that will call into the + // memory debugger which engages the `alloc_table_mutex` it self causing a dead-lock. We restore + // the flag, if it was set after the fact. + DN_HeapFlags flags_copy = arena->mem->heap.flags; + { + if (arena->mem->flags & DN_MemFlags_Heap) + arena->mem->heap.flags &= ~DN_HeapFlags_ExcludeFromMemDebugger; + + // NOTE: Allocate memory + result.count = 0; + result.data = DN_ArenaNewArrayZ(arena, DN_MemDebuggerSnapshotPtrData, count_req); + } + arena->mem->heap.flags = flags_copy; + + // NOTE: Snapshot the allocation table + if (result.data) { + for (DN_ForItSize(it, DN_MemDebuggerKV, debugger->alloc_table_kvs, debugger->alloc_table.max)) { + DN_MemDebuggerKV *kv = it.data; + if (!DN_HTableHashIsValue(kv->hash)) + continue; + + DN_MemDebuggerPtrData const *alloc = &kv->value; + if (alloc->flags & DN_MemDebuggerPtrFlag_Freed) { + result.freed_count++; + result.freed_bytes += alloc->size; + } else { + DN_MemDebuggerSnapshotPtrData *data = DN_PArrayMakeZ(result.data, &result.count, count_req); + data->ptr = alloc->ptr; + data->size = alloc->size; + data->flags = alloc->flags; + // NOTE: The strings in the call site are in the data-segment so shallow copy is fine + data->call_site = alloc->call_site; + data->stack_trace = DN_Str8FromStr8Arena(alloc->stack_trace, arena); + result.active_bytes += data->size; + } + } + } + } + DN_TicketMutexEnd(&debugger->alloc_table_mutex); + return result; +} + +bool DN_MemDebuggerSnapshotPtrDataQSortComparePtrLT(void const *lhs, void const *rhs, void*) +{ + DN_MemDebuggerSnapshotPtrData const *lhs_it = DN_Cast(DN_MemDebuggerSnapshotPtrData *) lhs; + DN_MemDebuggerSnapshotPtrData const *rhs_it = DN_Cast(DN_MemDebuggerSnapshotPtrData *) rhs; + bool result = DN_Cast(DN_UPtr) lhs_it->ptr < DN_Cast(DN_UPtr) rhs_it->ptr; + return result; +} + +DN_API void DN_MemDebuggerAlloc_(DN_MemDebugger *debugger, void *ptr, DN_USize size, bool leak_permitted, DN_CallSite call_site) { if (!ptr) return; - DN_TicketMutexBegin(&leak->alloc_table_mutex); + DN_TicketMutexBegin(&debugger->alloc_table_mutex); - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_HTable* alloc_table = &leak->alloc_table; - DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; - DN_HTableAddResult alloc_entry = DN_HTableMake(alloc_table, &uptr); - DN_LeakAlloc* alloc = DN_Cast(DN_LeakAlloc *)alloc_entry.slot.value; + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_HTable* alloc_table = &debugger->alloc_table; + DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; + DN_HTableAddResult alloc_entry = DN_HTableMake(alloc_table, &uptr); + DN_MemDebuggerPtrData* alloc = DN_Cast(DN_MemDebuggerPtrData *)alloc_entry.slot.value; if (alloc_entry.existed) { - if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { + if ((alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0) { DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); DN_AssertAlwaysF( - alloc->flags & DN_LeakAllocFlag_Freed, + alloc->flags & DN_MemDebuggerPtrFlag_Freed, "This pointer is already in the leak tracker, however it has not been freed yet. This " "same pointer is being ask to be tracked twice in the allocation table, e.g. one if its " "previous free calls has not being marked freed with an equivalent call to " - "DN_LeakTrackDealloc()\n" + "DN_MemDebuggerDealloc()\n" "\n" "The pointer (0x%p) originally allocated %.*s at:\n" "\n" @@ -11647,42 +12154,46 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b } // NOTE: Pointer was reused, clean up the prior entry - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.count; - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.count; + debugger->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.count; + debugger->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.count; DN_OS_MemDealloc(alloc->stack_trace.data); DN_OS_MemDealloc(alloc->freed_stack_trace.data); *alloc = {}; } - alloc->ptr = ptr; - alloc->size = size; - alloc->stack_trace = stack_trace; - alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.count; - DN_TicketMutexEnd(&leak->alloc_table_mutex); + alloc->ptr = ptr; + alloc->size = size; + alloc->stack_trace = stack_trace; + // NOTE: Strings inside the callsite are c-string literals stored in the data segment. So no deep + // copy is needed. + // TODO: Except, if we do DLL reloading, that's going to unload the string. + alloc->call_site = call_site; + alloc->flags |= leak_permitted ? DN_MemDebuggerPtrFlag_LeakPermitted : 0; + debugger->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.count; + DN_TicketMutexEnd(&debugger->alloc_table_mutex); } -DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) +DN_API void DN_MemDebuggerDealloc_(DN_MemDebugger *debugger, void *ptr) { if (!ptr) return; - DN_TicketMutexBegin(&leak->alloc_table_mutex); + DN_TicketMutexBegin(&debugger->alloc_table_mutex); - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_HTable* alloc_table = &leak->alloc_table; - DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; - DN_LeakAlloc* alloc = DN_Cast(DN_LeakAlloc *)DN_HTableValueFromFind(alloc_table, &uptr); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_HTable* alloc_table = &debugger->alloc_table; + DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; + DN_MemDebuggerPtrData* alloc = DN_Cast(DN_MemDebuggerPtrData *)DN_HTableValueFromFind(alloc_table, &uptr); DN_AssertAlwaysF(alloc, "Allocated pointer can not be removed as it does not exist in the " "allocation table. When this memory was allocated, the pointer was " "not added to the allocation table [ptr=%p]", ptr); - if (alloc->flags & DN_LeakAllocFlag_Freed) { + if (alloc->flags & DN_MemDebuggerPtrFlag_Freed) { DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); - DN_AssertAlwaysF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, + DN_AssertAlwaysF((alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0, "Double free detected, pointer to free was already marked " "as freed. Either the pointer was reallocated but not " "traced, or, the pointer was freed twice.\n" @@ -11706,24 +12217,24 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) } DN_Assert(alloc->freed_stack_trace.count == 0); - alloc->flags |= DN_LeakAllocFlag_Freed; - alloc->freed_stack_trace = stack_trace; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.count; - DN_TicketMutexEnd(&leak->alloc_table_mutex); + alloc->flags |= DN_MemDebuggerPtrFlag_Freed; + alloc->freed_stack_trace = stack_trace; + debugger->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.count; + DN_TicketMutexEnd(&debugger->alloc_table_mutex); } -DN_API void DN_LeakDump_(DN_LeakTracker *leak) +DN_API void DN_MemDebuggerDump_(DN_MemDebugger *debugger) { DN_U64 leak_count = 0; DN_U64 leaked_bytes = 0; - for (DN_ForItSize(it, DN_LeakTrackerKV, leak->alloc_table_kvs, leak->alloc_table.max)) { - DN_LeakTrackerKV *kv = it.data; + for (DN_ForItSize(it, DN_MemDebuggerKV, debugger->alloc_table_kvs, debugger->alloc_table.max)) { + DN_MemDebuggerKV *kv = it.data; if (!DN_HTableHashIsValue(kv->hash)) continue; - DN_LeakAlloc *alloc = &kv->value; - bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0; - bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted); + DN_MemDebuggerPtrData *alloc = &kv->value; + bool alloc_leaked = (alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0; + bool leak_permitted = (alloc->flags & DN_MemDebuggerPtrFlag_LeakPermitted); if (alloc_leaked && !leak_permitted) { leaked_bytes += alloc->size; leak_count++; @@ -12002,17 +12513,17 @@ DN_API DN_Heap DN_OS_HeapInitDefault() return result; } -DN_API DN_Arena DN_OS_ArenaFromHeapVirtual(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags) +DN_API DN_Arena DN_OS_ArenaFromHeapVirtualCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_CallSite call_site) { DN_Heap heap = DN_OS_HeapInitVirtual(); - DN_Arena result = DN_ArenaFromHeap(reserve, commit, flags, heap); + DN_Arena result = DN_ArenaFromHeapCallSite(reserve, commit, flags, heap, call_site); return result; } -DN_API DN_Arena DN_OS_ArenaFromHeapBasic(DN_U64 size, DN_MemFlags flags) +DN_API DN_Arena DN_OS_ArenaFromHeapBasicCallSite(DN_U64 size, DN_MemFlags flags, DN_CallSite call_site) { DN_Heap heap = DN_OS_HeapInitBasic(); - DN_Arena result = DN_ArenaFromHeap(/*reserve=*/ size, /*commit=*/ size, flags, heap); + DN_Arena result = DN_ArenaFromHeapCallSite(/*reserve=*/ size, /*commit=*/ size, flags, heap, call_site); return result; } @@ -12054,6 +12565,19 @@ DN_API DN_Str8 DN_OS_ExeDir(DN_Arena *arena) return result; } +DN_API void DN_OS_OpenUrl(DN_Str8 url) +{ + #if defined(DN_PLATFORM_WIN32) + DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0); + DN_Str16 url16 = DN_OS_W32Str8ToStr16(&scratch.arena, url); + ShellExecuteW(0, L"open", (WCHAR *)url16.data, 0, 0, SW_SHOWNORMAL); + DN_TcScratchEnd(&scratch); + #else + (void)url; + DN_AssertInvalidCodePathF("Unimplemented"); + #endif +} + // NOTE: Counters DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end) { @@ -12404,6 +12928,80 @@ DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Str8Slice cmd_line, DN_OSExecArgs ar return result; } +DN_API void DN_OS_WindowMaximise(DN_OSWindow *window, DN_OSWindowMaximise maximise) +{ + DN_OSWindowMaximise is_maximised = DN_OS_WindowIsMaximised(window); + if (is_maximised != maximise) { + DN_OSWindowShow show = DN_OSWindowShow_Nil; + switch (maximise) { + case DN_OSWindowMaximise_No: show = DN_OSWindowShow_Restore; break; + case DN_OSWindowMaximise_Yes: show = DN_OSWindowShow_Maximise; break; + } + DN_OS_WindowShow(window, show); + } +} + +DN_API void DN_OS_WindowMinimise(DN_OSWindow *window, DN_OSWindowMinimise minimise) +{ + DN_OSWindowMinimise is_minimised = DN_OS_WindowIsMinimised(window); + if (is_minimised != minimise) { + DN_OSWindowShow show = DN_OSWindowShow_Nil; + switch (minimise) { + case DN_OSWindowMinimise_No: show = DN_OSWindowShow_Restore; break; + case DN_OSWindowMinimise_Yes: show = DN_OSWindowShow_Minimise; break; + } + DN_OS_WindowShow(window, show); + } +} + +DN_API void DN_OS_WindowCustomTitlebarSet(DN_OSWindow *window, bool custom_titlebar) +{ + #if defined(DN_PLATFORM_WIN32) + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + DN_OSW32Core *w32 = DN_OS_W32GetCore(); + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, DN_TcMainArena()); + if (w32_window->custom_title_bar != custom_titlebar) { + w32_window->custom_title_bar = custom_titlebar; + + // NOTE: We force a NCCALCSIZE message to be sent here which triggers the custom border logic + // For this to work the windows procedure must be handling NCCALCSIZE and NCHITTEST otherwise this + // is going to do nothing. + // + // For example in sokol you can do something like this to equip a window proc that handles + // custom titlebars. + // + // HWND hwnd = DN_Cast(HWND) sapp_win32_get_hwnd(); + // DN_OSW32Core *w32 = DN_OS_W32GetCore(); + // w32->fallback_wnd_proc = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC); + // SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)DN_OS_W32WindowProcCustomTitlebar); + // + // In your code, you should set w32_window->custom_title_bar_rect_that_is_draggable to the + // region of the title bar that can be dragged to move the window around (excluding any buttons + // or controls that shouldn't respond to this interaction). + SetWindowPos(w32_window->hwnd, NULL, 0, 0, 0, 0, + SWP_FRAMECHANGED | // Recalculate frame (triggers WM_NCCALCSIZE) + SWP_NOMOVE | // Don't move window + SWP_NOSIZE | // Don't resize window + SWP_NOZORDER | // Don't change Z order + SWP_NOACTIVATE | // Don't activate window + SWP_NOOWNERZORDER); // Don't change owner Z order + + if (w32_window->custom_title_bar) { + // NOTE: Call DwmExtendFrameIntoClientArea with the margins (-1,-1,-1,-1), which, magically, + // does, something, related to how windows treats the borders. LLM says it enables DWM + // shadow/composition. + // + // From Freya Holmer + // https://discord.com/channels/239737791225790464/1294659776449609788/1294661218513977445 + MARGINS margins = {-1, -1, -1, -1}; + DwmExtendFrameIntoClientArea(w32_window->hwnd, &margins); + } + } + #else + DN_AssertInvalidCodePath; + #endif +} + static void DN_OS_ThreadExecute_(void *user_context) { DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; @@ -13090,7 +13688,9 @@ DN_API DN_Str8 DN_Str8FromStackTraceNowArena(DN_Arena *arena, DN_U16 limit, DN_U DN_API DN_Str8 DN_Str8FromStackTraceNowHeap(DN_U16 limit, DN_U16 skip) { - DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_Kilobytes(64), DN_MemFlags_NoAllocTrack, DN_OS_HeapInitBasic()); + DN_Heap heap = DN_OS_HeapInitBasic(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_Kilobytes(64), DN_MemFlags_Nil, heap, "DN Stack Trace Arena"); DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); DN_StackTrace walk = DN_StackTraceFromArena(&arena, limit); DN_StackTraceAddToStr8Builder_(&walk, &builder, skip); @@ -14462,6 +15062,53 @@ DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv) } } +DN_API DN_OSWindowMinimise DN_OS_WindowIsMinimised(DN_OSWindow *window) +{ + (void)window; + DN_AssertInvalidCodePath; + return DN_OSWindowMinimise_No; +} + +DN_API DN_OSWindowMaximise DN_OS_WindowIsMaximised(DN_OSWindow *window) +{ + (void)window; + DN_AssertInvalidCodePath; + return DN_OSWindowMaximise_No; +} + +DN_API void DN_OS_WindowSetTitle(DN_OSWindow *window, DN_Str8 title) +{ + (void)window; + (void)title; + DN_AssertInvalidCodePath; +} + +DN_API void DN_OS_WindowShow(DN_OSWindow *window, DN_OSWindowShow show) +{ + (void)window; + (void)show; + DN_AssertInvalidCodePath; +} + +DN_API void DN_OS_WindowBringToFront(DN_OSWindow *window) +{ + (void)window; + DN_AssertInvalidCodePath; +} + +DN_API void DN_OS_WindowFocus(DN_OSWindow *window) +{ + (void)window; + DN_AssertInvalidCodePath; +} + +DN_API bool DN_OS_WindowIsFocused(DN_OSWindow *window) +{ + (void)window; + DN_AssertInvalidCodePath; + return false; +} + // NOTE: DN_OSThread static void *DN_OS_ThreadFunc_(void *user_context) { @@ -14638,6 +15285,7 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus() // #define DN_WITH_OS 1 // #include "../dn.h" // #include "dn_os_w32.h" +// #include // #endif // NOTE: DN_Mem @@ -15692,6 +16340,36 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs return result; } +DN_API void DN_OS_W32Init(DN_OSW32Core *w32) +{ + InitializeCriticalSection(&w32->sync_primitive_free_list_mutex); + QueryPerformanceFrequency(&w32->qpc_frequency); + + // NOTE: DPI + HMODULE user32 = LoadLibraryA("user32.dll"); + if (user32) { + w32->get_dpi_for_window = (DN_OSW32GetDpiForWindowFunc* )GetProcAddress(user32, "GetDpiForWindow"); + w32->get_dpi_for_monitor = (DN_OSW32GetDpiForMonitorFunc * )GetProcAddress(user32, "GetDpiForMonitor"); + w32->get_system_metrics_for_dpi = (DN_OSW32GetSystemMetricsForDpiFunc *)GetProcAddress(user32, "GetSystemMetricsForDpi"); + FreeLibrary(user32); + } + + // NOTE: SetThreadDesc + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) { + w32->set_thread_description = DN_Cast(DN_OSW32SetThreadDescriptionFunc *) GetProcAddress(kernel32, "SetThreadDescription"); + FreeLibrary(kernel32); + } + + // NOTE: win32 bcrypt + wchar_t const BCRYPT_ALGORITHM[] = L"RNG"; + long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/); + if (w32->bcrypt_rng_handle && init_status == 0) + w32->bcrypt_init_success = true; + else + DN_LogErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status); +} + DN_API DN_OSW32Core *DN_OS_W32GetCore() { DN_Core *dn = DN_Get(); @@ -15923,6 +16601,70 @@ DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv) } } +DN_API DN_OSWindowMinimise DN_OS_WindowIsMinimised(DN_OSWindow *window) +{ + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + DN_OSWindowMinimise result = DN_OSWindowMinimise_No; + if (hwnd && IsIconic(hwnd) != 0) + result = DN_OSWindowMinimise_Yes; + return result; +} + +DN_API DN_OSWindowMaximise DN_OS_WindowIsMaximised(DN_OSWindow *window) +{ + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + DN_OSWindowMaximise result = DN_OSWindowMaximise_No; + if (hwnd && IsZoomed(hwnd) != 0) + result = DN_OSWindowMaximise_Yes; + return result; +} + +DN_API void DN_OS_WindowSetTitle(DN_OSWindow *window, DN_Str8 title) +{ + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + if (hwnd) { + DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0); + DN_Str16 title16 = DN_OS_W32Str8ToStr16(&scratch.arena, title); + SetWindowTextW(hwnd, (WCHAR *)title16.data); + DN_TcScratchEnd(&scratch); + } +} + +DN_API void DN_OS_WindowShow(DN_OSWindow *window, DN_OSWindowShow show) +{ + switch (show) { + case DN_OSWindowShow_Nil: break; + case DN_OSWindowShow_Restore: ShowWindow(DN_Cast(HWND)window->handle, SW_RESTORE); break; + case DN_OSWindowShow_Minimise: ShowWindow(DN_Cast(HWND)window->handle, SW_MINIMIZE); break; + case DN_OSWindowShow_Maximise: ShowWindow(DN_Cast(HWND)window->handle, SW_MAXIMIZE); break; + } +} + +DN_API void DN_OS_WindowBringToFront(DN_OSWindow *window) +{ + HWND hwnd = window ? DN_Cast(HWND)window->handle : nullptr; + if (hwnd) + BringWindowToTop(hwnd); +} + +DN_API void DN_OS_WindowFocus(DN_OSWindow *window) +{ + HWND hwnd = window ? DN_Cast(HWND)window->handle : nullptr; + if (hwnd) { + SetForegroundWindow(hwnd); + SetFocus(hwnd); + } +} + +DN_API bool DN_OS_WindowIsFocused(DN_OSWindow *window) +{ + bool result = false; + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + if (hwnd) + result = hwnd == GetForegroundWindow(); + return result; +} + static DWORD __stdcall DN_OS_ThreadFunc_(void *user_context) { DN_OS_ThreadExecute_(user_context); @@ -16027,6 +16769,160 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) DN_TcScratchEnd(&scratch); } +DN_API DN_OSW32Window* DN_OS_W32WindowFromHwndMaybeAlloc(DN_OSW32Core *w32, HWND hwnd, DN_Arena *arena) +{ + DN_OSW32Window *result = nullptr; + for (DN_OSW32Window *check = w32->windows; !result && check; check = check->next) { + if (check->hwnd == hwnd) + result = check; + } + + if (!result && arena) { + result = DN_ArenaNewZ(arena, DN_OSW32Window); + if (result) { + result->hwnd = hwnd; + if (w32->windows) + w32->windows->next = result; + result->next = w32->windows; + w32->windows = result; + } + } + return result; +} + +DN_API LRESULT __stdcall DN_OS_W32WindowProcCustomTitlebar(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) +{ + DN_OSW32Core *w32 = DN_OS_W32GetCore(); + DN_AssertF(w32, "Using this window proc requires that DN's OS layer has been initialised"); + + LRESULT result = 0; + bool handled = true; + switch (msg) { + // NOTE: Sent when the size and position of a window's client area must be calculated. By + // processing this message, an application can control the content of the window's client area + // when the size or position of the window changes. + case WM_NCCALCSIZE: { + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, nullptr); + if (w32_window && w32_window->custom_title_bar) { + // NOTE: Code adapted from raddebugger + // https://github.com/EpicGames/raddebugger/blob/7a5a649b89f6a06abdbaee61540468fca9db58f9/src/win32/window_manager/win32_window_manager.c#L668 + // + // The goal here is to subtract away the title bar from the window and restore the pixel + // border that native Windows renders windows with. Note that on Windows 11 a non-maximised + // window has rounded corners which eats into the available client space that is normally + // otherwise eaten into from the native titlebar. + DWORD window_style = GetWindowLongW(hwnd, GWL_STYLE); + bool is_fullscreen = !(window_style & WS_OVERLAPPEDWINDOW); + if (is_fullscreen) { + handled = false; + } else { + DN_UInt dpi = w32->get_dpi_for_window ? w32->get_dpi_for_window(hwnd) : 96; + int frame_x = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXFRAME, dpi) : GetSystemMetrics(SM_CXFRAME); + int frame_y = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYFRAME, dpi) : GetSystemMetrics(SM_CYFRAME); + int padding = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXPADDEDBORDER, dpi) : GetSystemMetrics(SM_CXPADDEDBORDER); + + RECT *rect = w_param == 0 ? DN_Cast(RECT *)l_param : (DN_Cast(NCCALCSIZE_PARAMS *)l_param)->rgrc; + rect->right -= frame_x + padding; + rect->left += frame_x + padding; + rect->bottom -= frame_y + padding; + bool is_maximised = IsZoomed(hwnd) != 0; + if (is_maximised) { + rect->top += frame_y + padding; + // If we do not do this hidden taskbar can not be unhidden on mouse hover + // Unfortunately it can create an ugly bottom border when maximized... + rect->bottom -= 1; + } else { + int edge_height = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYEDGE, dpi) : GetSystemMetrics(SM_CYEDGE); + rect->top += edge_height; + } + } + } else { + handled = false; + } + } break; + + // NOTE: Sent to a window in order to determine what part of the window corresponds to a + // particular screen coordinate. This can happen, for example, when the cursor moves, when a + // mouse button is pressed or released, or in response to a call to a function such as + // WindowFromPoint. If the mouse is not captured, the message is sent to the window beneath + // the cursor. Otherwise, the message is sent to the window that has captured the mouse. + case WM_NCHITTEST: { + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, nullptr); + if (w32_window && w32_window->custom_title_bar) { + // NOTE: Code adapted from raddebugger + // https://github.com/EpicGames/raddebugger/blob/7a5a649b89f6a06abdbaee61540468fca9db58f9/src/win32/window_manager/win32_window_manager.c#L668 + // + // Since we removed the titlebar from the window, the resize handlers/grabbers at the top of + // the window are no longer functional. We have to reimplement it ourselves. + DWORD window_style = GetWindowLongW(hwnd, GWL_STYLE); + bool is_fullscreen = !(window_style & WS_OVERLAPPEDWINDOW); + handled = false; + if (!is_fullscreen) { + // Let the default procedure handle resizing areas + if (w32->fallback_wnd_proc) + result = CallWindowProcW(w32->fallback_wnd_proc, hwnd, msg, w_param, l_param); + else + result = DefWindowProcW(hwnd, msg, w_param, l_param); + + switch (result) { + case HTNOWHERE: + case HTRIGHT: + case HTLEFT: + case HTTOPLEFT: + case HTTOPRIGHT: + case HTBOTTOMRIGHT: + case HTBOTTOM: + case HTBOTTOMLEFT: { + handled = true; + } break; + } + + if (!handled) { + POINT pos_monitor = {}; + pos_monitor.x = GET_X_LPARAM(l_param); + pos_monitor.y = GET_Y_LPARAM(l_param); + handled = true; + + POINT pos_client = pos_monitor; + ScreenToClient(hwnd, &pos_client); + + DN_V2F32 title_bar_min = w32_window->custom_title_bar_rect_that_is_draggable.pos; + DN_V2F32 title_bar_max = title_bar_min + w32_window->custom_title_bar_rect_that_is_draggable.size; + DN_UInt dpi = w32->get_dpi_for_window ? w32->get_dpi_for_window(hwnd) : 96; + int frame_y = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYFRAME, dpi) : GetSystemMetrics(SM_CYFRAME); + int padding = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXPADDEDBORDER, dpi) : GetSystemMetrics(SM_CXPADDEDBORDER); + bool is_over_top_resize = pos_client.y >= 0 && pos_client.y < (frame_y + padding); + bool is_over_title_bar = pos_client.x >= title_bar_min.x && pos_client.x < title_bar_max.x && + pos_client.y >= title_bar_min.y && pos_client.y < title_bar_max.y; + bool is_maximised = IsZoomed(hwnd) != 0; + if (!is_maximised && is_over_top_resize) + result = HTTOP; + else if (is_over_title_bar) + result = HTCAPTION; + else + result = HTCLIENT; + } + } + } else { + handled = false; + } + } break; + + default: { + handled = false; + } break; + } + + if (!handled) { + if (w32->fallback_wnd_proc) { + result = CallWindowProcW(w32->fallback_wnd_proc, hwnd, msg, w_param, l_param); + } else { + result = DefWindowProcW(hwnd, msg, w_param, l_param); + } + } + return result; +} + DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code) { DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -16485,7 +17381,7 @@ DN_NETRequestHandle DN_NET_SetupRequest(DN_NETRequest *request, DN_Str8 url, DN_ "free-list and reusing it. This is so that we centralise the one place that we " "reinitialise the temp arena for the request into this codepath."); else - request->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(1), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + request->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(1), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "DN NET Request MemList"); request->arena = DN_ArenaTempBeginFromMemList(&request->mem); request->type = type; diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index af9995a..806fd9e 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -1,8 +1,12 @@ -// Generated by the DN single header generator 2026-07-31 22:40:42 +// Generated by the DN single header generator 2026-09-29 22:28:11 #if !defined(DN_H) #define DN_H +// DN: Single header generator commented out => #if defined(_CLANGD) +// #define DN_WITH_OS 1 +// #endif + // NOTE: DN // Getting Started @@ -180,11 +184,17 @@ // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. +// Memory Debugger +// Set `DN_MEM_DEBUGGER` to `1` to enable memory debugging which enables the `DN_Core`'s +// `mem_debugger` field, tracks all allocations in the application and reports leaks. +// +// #define DN_MEM_DEBUGGER 1 + // Paranoia Level // Set the `DN_PARANOIA_LEVEL` to an integer value to enable various validation layers and // error checking mechanisms in the codebase and primitives exposed by the library -// (default: PARANOID level 1 in debug builds, 0 otherwise) -// paranoia level 0 in release builds and level 1 for debug. +// (default: PARANOIA level 1 in debug builds, 0 otherwise) paranoia level 0 in release builds +// and level 1 for debug. // // #define DN_PARANOIA_LEVEL 1 // @@ -556,11 +566,11 @@ #define DN_ForIndexU(index, count) DN_USize index = 0; index < count; index++ #define DN_ForIndexI(index, count) DN_ISize index = 0; index < count; index++ -#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index -#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) - 1, &(array)[count - 1]}; it.index < (count); it.index--, it.data = (array) + it.index -#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index -#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) -#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index +#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index +#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) ? (count) - 1 : 0, (count) ? &(array)[count - 1] : 0}; it.index < (count); it.index--, it.data = (array) + it.index +#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index +#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) +#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index #define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1)) #define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1)) @@ -784,6 +794,19 @@ typedef enum DN_ZMem { DN_ZMem_Yes, // Memory should be zero-ed out before giving to the callee } DN_ZMem; +typedef enum DN_Side { + DN_Side_Left, + DN_Side_Right, + DN_Side_Top, + DN_Side_Bottom, + DN_Side_Count, +} DN_Side; + +typedef enum DN_AddType { + DN_AddType_Prepend, + DN_AddType_Append, +} DN_AddType; + typedef struct DN_Str8 DN_Str8; struct DN_Str8 { @@ -993,12 +1016,14 @@ DN_MSVC_WARNING_POP typedef struct DN_CallSite DN_CallSite; struct DN_CallSite { + DN_Str8 name; DN_Str8 file; DN_Str8 function; DN_U32 line; }; -#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(""), DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNowNamed(name_cstr) DN_Literal(DN_CallSite){DN_Str8Lit(name_cstr), DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } #if defined(__cplusplus) template @@ -1124,9 +1149,9 @@ enum DN_MemPage_ // This flag must only be used in DN_Mem_Protect DN_MemPage_Guard = 1 << 3, - // If leak tracing is enabled, this flag will allow the allocation recorded - // from the reserve call to be leaked, e.g. not printed when leaks are - // dumped to the console. + // If memory debugging is enabled, this flag will allow the allocation + // recorded from the reserve call to be leaked, e.g. not printed when leaks + // are dumped to the console. DN_MemPage_AllocRecordLeakPermitted = 1 << 4, // If leak tracing is enabled this flag will prevent any allocation record @@ -1158,15 +1183,31 @@ typedef enum DN_HeapType { DN_HeapType_Virtual, } DN_HeapType; +typedef DN_U32 DN_HeapAllocFlag; +enum DN_HeapAllocFlag_ { + DN_HeapAllocFlag_Nil = 0, + DN_HeapAllocFlag_MemDebuggerCanLeakPtr = 1 << 0, + DN_HeapAllocFlag_MemDebuggerExcludePtr = 1 << 1, +}; + +typedef DN_U32 DN_HeapFlags; +enum DN_HeapFlags_ { + DN_HeapFlags_Nil = 0, + DN_HeapFlags_ExcludeFromMemDebugger = 1 << 0, + DN_HeapFlags_MemDebuggerCanLeakPtr = 1 << 1, +}; + typedef void *(DN_HeapBasicAllocFunc) (DN_USize count); typedef void (DN_HeapBasicDeallocFunc)(void *ptr); typedef void *(DN_HeapVirtualReserveFunc)(DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); typedef bool (DN_HeapVirtualCommitFunc)(void *ptr, DN_USize count, DN_U32 page_flags); typedef void (DN_HeapVirtualReleaseFunc)(void *ptr, DN_USize count); + typedef struct DN_Heap DN_Heap; struct DN_Heap { + DN_HeapFlags flags; DN_HeapType type; DN_USize bytes_alloc_total; DN_USize bytes_alloc; @@ -1213,8 +1254,6 @@ enum DN_MemFlags_ DN_MemFlags_Nil = 0, DN_MemFlags_NoGrow = 1 << 0, DN_MemFlags_NoPoison = 1 << 1, - DN_MemFlags_NoAllocTrack = 1 << 2, - DN_MemFlags_AllocCanLeak = 1 << 3, DN_MemFlags_SimAlloc = 1 << 4, // NOTE: Records stack traces of temp memory regions on construction to provide more diagnostics @@ -1244,12 +1283,16 @@ enum DN_MemFlags_ typedef struct DN_MemList DN_MemList; struct DN_MemList { - DN_MemBlock* curr; + DN_MemBlock* curr; // Contiguous blocks of memory, chained for growth, parcelled for object allocation DN_MemFlags flags; - DN_Heap heap; + DN_Heap heap; // Backing allocator for the memory blocks DN_MemStats stats; DN_Str8 label; + #if DN_MEM_DEBUGGER + DN_USize mem_debugger_id; + #endif + #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_U32 uaf_guard_next_id; DN_U32 uaf_guard_active_id; @@ -1365,24 +1408,33 @@ struct DN_Pool DN_U8 align; }; -typedef struct DN_UTF8DecodeResult DN_UTF8DecodeResult; -struct DN_UTF8DecodeResult +typedef struct DN_Utf32FromResult DN_Utf32FromResult; +struct DN_Utf32FromResult { - bool success; - DN_Str8 remaining; - DN_U32 codepoint; + bool success; + DN_Str8 remaining; + DN_U32 codepoint; + DN_USize byte_count; // Number of bytes from the stream that was decoded }; -typedef struct DN_UTF8DecodeIterator DN_UTF8DecodeIterator; -struct DN_UTF8DecodeIterator +typedef struct DN_Utf32FromIterator DN_Utf32FromUtf8Iterator; +struct DN_Utf32FromIterator { bool init; bool success; DN_Str8 remaining; + DN_USize bytes_decoded_; + DN_USize bytes_decoded_total; DN_USize codepoint_index; DN_U32 codepoint; }; +enum DN_Utf32IterateDir +{ + DN_Utf32IterateDir_Forwards, + DN_Utf32IterateDir_Reverse, +}; + typedef DN_U32 DN_CodepointCountFlags; enum DN_CodepointCountFlags_ { @@ -1499,12 +1551,6 @@ struct DN_Str8Builder DN_USize count; // The number of links in the linked list of strings }; -enum DN_Str8BuilderAdd -{ - DN_Str8BuilderAdd_Append, - DN_Str8BuilderAdd_Prepend, -}; - #if !defined(DN_OsPathSeperator) #if defined(DN_PLATFORM_WIN32) #define DN_OsPathSeperator "\\" @@ -1626,6 +1672,97 @@ struct DN_Profiler DN_F64 frame_avg_tsc; }; +struct DN_Profiler2Marker +{ + DN_Str8 name; + DN_U64 tsc_begin; + DN_U64 tsc_end; + DN_U64 tsc_children_duration; + DN_CallSite call_site; + DN_U32 parent_index; + DN_U32 next_index; + DN_U32 first_child_index; + DN_U32 last_child_index; +}; + +struct DN_Profiler2Frame +{ + // NOTE: The frame number that this frame was part of. Frame number is incremented each time + // frame-begin is called. + DN_U32 frame_num; + + // NOTE: The markers array is not contiguous. We using a ring-buffer so accessing the marker must + // be done like `profiler->markers + ((frame->begin_offset + i) % profiler->markers_max)`. A + // helper function is available `DN_Profiler2MarkerFromFrameIndex(profiler, frame, i)` + DN_U32 begin_offset; + DN_U32 count; + DN_U32 max; + + // NOTE: The profiling marker that the profiler makes and closes when frame-begin is called. + // Encapsulates essentially the duration of 1 update loop in an interactive application. + DN_Profiler2Marker* begin_marker; +}; + +struct DN_Profiler2Zone +{ + struct DN_Profiler2* profiler; + DN_Profiler2Marker* marker; + bool ended; + bool scope_once; +}; + +enum DN_Profiler2State +{ + DN_Profiler2State_Run, + DN_Profiler2State_Paused, + DN_Profiler2State_UnpauseEnqueued, +}; + +struct DN_Profiler2 +{ + DN_Profiler2State state; + DN_U32 frame_num; + DN_ProfilerTscNowFunc* tsc_now; + DN_U64 tsc_frequency; + + DN_Profiler2Frame* frames; + DN_U32 frames_max; + DN_U32 frames_write_index_unwrapped; + + DN_Profiler2Marker* markers; + DN_U32 markers_max; + + DN_Profiler2Frame* active_frame; + DN_U32 active_parent_index; + + struct DN_Profiler2Zone frame_begin_zone; +}; + +struct DN_Profiler2Iterator +{ + bool init; + DN_USize iteration_count; + DN_U32 marker_index; + DN_U32 last_visited_index; + DN_Profiler2Marker* marker; +}; + +struct DN_Profiler2FunctionTime +{ + DN_CallSite call_site; + DN_Str8 name; + DN_USize hit_count; + DN_U64 tsc_total; + DN_U64 tsc_children_total; +}; + +struct DN_Profiler2FunctionTimes +{ + DN_Profiler2FunctionTime *data; + DN_USize count; + DN_USize max; +}; + typedef bool (DN_QSortCompareFunc) (void const *lhs, void const *rhs, void *user_context); typedef bool (DN_BSearchLessThanFunc)(void const *lhs, void const *rhs, void *user_context); enum DN_BSearchType @@ -1752,8 +1889,13 @@ typedef enum DN_TcDeinitArenas { typedef struct DN_Pcg32 DN_Pcg32; struct DN_Pcg32 { DN_U64 state; }; -typedef struct DN_Murmur3 DN_Murmur3; -struct DN_Murmur3 { DN_U64 e[2]; }; +typedef struct DN_Murmur3Context DN_Murmur3Context; +struct DN_Murmur3Context +{ + DN_U32 it; + DN_U32 carry; + DN_U32 bytes_count; +}; typedef enum DN_LogType { DN_LogType_Debug, @@ -1932,18 +2074,11 @@ typedef enum DN_RectCutClip { DN_RectCutClip_Yes, } DN_RectCutClip; -typedef enum DN_RectCutSide { - DN_RectCutSide_Left, - DN_RectCutSide_Right, - DN_RectCutSide_Top, - DN_RectCutSide_Bottom, -} DN_RectCutSide; - typedef struct DN_RectCut DN_RectCut; struct DN_RectCut { - DN_Rect* rect; - DN_RectCutSide side; + DN_Rect* rect; + DN_Side side; }; typedef struct DN_RaycastV2 DN_RaycastV2; @@ -1968,11 +2103,6 @@ typedef enum DN_ArrayErase { DN_ArrayErase_Stable, } DN_ArrayErase; -typedef enum DN_ArrayAdd { - DN_ArrayAdd_Append, - DN_ArrayAdd_Prepend, -} DN_ArrayAdd; - typedef struct DN_ArrayEraseResult DN_ArrayEraseResult; struct DN_ArrayEraseResult { @@ -2106,39 +2236,56 @@ enum DN_HTableAllowTombstone DN_HTableAllowTombstone_Yes, }; -enum DN_LeakAllocFlag +enum DN_MemDebuggerPtrFlag { - DN_LeakAllocFlag_Freed = 1 << 0, - DN_LeakAllocFlag_LeakPermitted = 1 << 1, + DN_MemDebuggerPtrFlag_Freed = 1 << 0, + DN_MemDebuggerPtrFlag_LeakPermitted = 1 << 1, }; -typedef struct DN_LeakAlloc DN_LeakAlloc; -struct DN_LeakAlloc +typedef struct DN_MemDebuggerPtrData DN_MemDebuggerPtrData; +struct DN_MemDebuggerPtrData { - void *ptr; // 8 Pointer to the allocation being tracked - DN_USize size; // 16 Size of the allocation - DN_USize freed_size; // 24 Store the size of the allocation when it is freed - DN_Str8 stack_trace; // 40 Stack trace at the point of allocation - DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed - DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` + DN_CallSite call_site; + DN_U16 flags; // Bit flags from `DN_MemDebuggerPtrFlag` + void* ptr; // Pointer to the allocation being tracked + DN_USize size; // Size of the allocation + DN_USize freed_size; // Store the size of the allocation when it is freed + DN_Str8 stack_trace; // Stack trace at the point of allocation + DN_Str8 freed_stack_trace; // Stack trace of where the allocation was freed }; -// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get -// expensive. Enforce that there is no unexpected padding. -DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively - -typedef struct DN_LeakTrackerKV DN_LeakTrackerKV; -struct DN_LeakTrackerKV +typedef struct DN_MemDebuggerKV DN_MemDebuggerKV; +struct DN_MemDebuggerKV { - DN_HTableHashType hash; - DN_UPtr key; - DN_LeakAlloc value; + DN_HTableHashType hash; + DN_UPtr key; + DN_MemDebuggerPtrData value; }; -typedef struct DN_LeakTracker DN_LeakTracker; -struct DN_LeakTracker +typedef struct DN_MemDebuggerSnapshotPtrData DN_MemDebuggerSnapshotPtrData; +struct DN_MemDebuggerSnapshotPtrData { - DN_LeakTrackerKV *alloc_table_kvs; + void* ptr; + DN_USize size; + DN_U16 flags; + DN_CallSite call_site; + DN_Str8 stack_trace; +}; + +typedef struct DN_MemDebuggerSnapshot DN_MemDebuggerSnapshot; +struct DN_MemDebuggerSnapshot +{ + DN_MemDebuggerSnapshotPtrData *data; + DN_USize count; + DN_U64 active_bytes; + DN_U64 freed_count; + DN_U64 freed_bytes; +}; + +typedef struct DN_MemDebugger DN_MemDebugger; +struct DN_MemDebugger +{ + DN_MemDebuggerKV *alloc_table_kvs; DN_HTable alloc_table; DN_TicketMutex alloc_table_mutex; DN_U64 alloc_table_bytes_allocated_for_stack_traces; @@ -2152,7 +2299,6 @@ enum DN_InitFlags_ // NOTE: Query the OS for information and enable functionality that requires the OS (like virtual // memory APIs, mutexes, secure RNG) as well as per-thread persistent and scratch allocators. DN_InitFlags_OS = (1 << 0), - DN_InitFlags_LeakTracker = (1 << 1) | DN_InitFlags_OS, DN_InitFlags_LogLibFeatures = (1 << 2), DN_InitFlags_LogCPUFeatures = (1 << 3) | DN_InitFlags_OS, DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures, @@ -4379,6 +4525,31 @@ struct DN_OSConditionVariable DN_U64 handle; }; +struct DN_OSWindow +{ + DN_UPtr handle; +}; + +enum DN_OSWindowShow +{ + DN_OSWindowShow_Nil, + DN_OSWindowShow_Restore, + DN_OSWindowShow_Minimise, + DN_OSWindowShow_Maximise, +}; + +enum DN_OSWindowMaximise +{ + DN_OSWindowMaximise_No, + DN_OSWindowMaximise_Yes, +}; + +enum DN_OSWindowMinimise +{ + DN_OSWindowMinimise_No, + DN_OSWindowMinimise_Yes, +}; + typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *); typedef DN_U32 DN_OSThreadFlags; @@ -4552,7 +4723,8 @@ struct DN_Core DN_InitFlags init_flags; DN_TcCore main_tc; DN_USize mem_allocs_frame; - DN_LeakTracker leak; + DN_MemDebugger mem_debugger; + DN_USize mem_debugger_mem_list_id_counter; DN_LogType log_level_to_show_from; DN_LogPrintFunc* print_func; @@ -4749,14 +4921,17 @@ DN_API DN_MemStats DN_MemStatsSumArray // API // DN_HeapAlloc -// DN_HeapDealloc -// Allocate and deallocate memory. Note that `DN_HeapAlloc` takes reserve and commit even for -// the basic allocator. In the basic allocator which does not have a concept of reserving and -// committing, the `commit` value is ignored and the `reserve` amount is the amount that will be -// allocated. +// Allocate memory using the given heap's configured allocation functions. For a heap using the +// basic allocation routines, `commit` is ignored, `reserve` is the amount of bytes to be +// allocated and implicitly committed. For virtual memory then `reserve` is the amount of +// address space to reserve for the application from the OS. `commit` is the amount of memory in +// pages that the OS will give your application. + +#define DN_MemMetadataInit(name_cstr) DN_Literal(DN_MemMetadata){DN_CallSiteNow, DN_Str8Lit(name_cstr)} DN_API DN_Heap DN_HeapInitBasic (DN_HeapBasicAllocFunc *basic_alloc, DN_HeapBasicDeallocFunc *basic_dealloc); DN_API DN_Heap DN_HeapInitVirtual (DN_U32 page_size, DN_HeapVirtualReserveFunc *virtual_reserve, DN_HeapVirtualCommitFunc *virtual_commit, DN_HeapVirtualReleaseFunc *virtual_release); -DN_API void* DN_HeapAlloc (DN_Heap *heap, DN_USize reserve, DN_USize commit); +DN_API void* DN_HeapAllocCallSite (DN_Heap *heap, DN_USize reserve, DN_USize commit, DN_HeapAllocFlag flags, DN_CallSite call_site); +#define DN_HeapAlloc(heap, reserve, commit, flags, name_cstr) DN_HeapAllocMetadata(heap, reserve, commit, flags, DN_MemMetadataInit(name_cstr)); DN_API void DN_HeapDealloc (DN_Heap *heap, void *ptr, DN_USize size); // NOTE: MemList @@ -4768,32 +4943,33 @@ DN_API void DN_HeapDealloc // // In general for 99% of use-cases you want an `Arena` and if some API is not exposed on the // `Arena` to then use the `MemList` API on the embedded `mem_list` backing the Arena. -DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); -DN_API DN_MemList DN_MemListFromHeap (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap); -DN_API void DN_MemListDeinit (DN_MemList *mem); -DN_API bool DN_MemListCommit (DN_MemList *mem, DN_U64 size); -DN_API bool DN_MemListCommitTo (DN_MemList *mem, DN_U64 pos); -DN_API bool DN_MemListGrow (DN_MemList *mem, DN_U64 reserve, DN_U64 commit); -DN_API void * DN_MemListAlloc (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); -DN_API void * DN_MemListAllocContiguous (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); -DN_API void * DN_MemListCopy (DN_MemList *mem, void const *data, DN_U64 size, uint8_t align); -DN_API void DN_MemListPopTo (DN_MemList *mem, DN_U64 init_used); -DN_API void DN_MemListPop (DN_MemList *mem, DN_U64 amount); -DN_API DN_U64 DN_MemListPos (DN_MemList const *mem); -DN_API void DN_MemListClear (DN_MemList *mem); -DN_API bool DN_MemListOwnsPtr (DN_MemList const *mem, void const *ptr); -DN_API DN_Str8x64 DN_MemListInfoStr8x64 (DN_MemListInfo info); -DN_API DN_MemListTemp DN_MemListTempBegin (DN_MemList *mem); -DN_API void DN_MemListTempEnd (DN_MemListTemp mem); -#define DN_MemListNew(arena, T, zmem) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), zmem) -#define DN_MemListNewZ(arena, T) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewContiguous(arena, T, zmem) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), zmem) -#define DN_MemListNewContiguousZ(arena, T) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewArray(arena, T, count, zmem) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), zmem) -#define DN_MemListNewArrayZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewArrayNoZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) -#define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) -#define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) +DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); +DN_API DN_MemList DN_MemListFromHeapCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site); +#define DN_MemListFromHeap(reserve, commit, flags, heap, name_cstr) DN_MemListFromHeapCallSite(reserve, commit, flags, heap, DN_CallSiteNowNamed(name_cstr)) +DN_API void DN_MemListDeinit (DN_MemList *mem); +DN_API bool DN_MemListCommit (DN_MemList *mem, DN_U64 size); +DN_API bool DN_MemListCommitTo (DN_MemList *mem, DN_U64 pos); +DN_API bool DN_MemListGrow (DN_MemList *mem, DN_U64 reserve, DN_U64 commit); +DN_API void * DN_MemListAlloc (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); +DN_API void * DN_MemListAllocContiguous (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); +DN_API void * DN_MemListCopy (DN_MemList *mem, void const *data, DN_U64 size, uint8_t align); +DN_API void DN_MemListPopTo (DN_MemList *mem, DN_U64 init_used); +DN_API void DN_MemListPop (DN_MemList *mem, DN_U64 amount); +DN_API DN_U64 DN_MemListPos (DN_MemList const *mem); +DN_API void DN_MemListClear (DN_MemList *mem); +DN_API bool DN_MemListOwnsPtr (DN_MemList const *mem, void const *ptr); +DN_API DN_Str8x64 DN_MemListInfoStr8x64 (DN_MemListInfo info); +DN_API DN_MemListTemp DN_MemListTempBegin (DN_MemList *mem); +DN_API void DN_MemListTempEnd (DN_MemListTemp mem); +#define DN_MemListNew(arena, T, zmem) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), zmem) +#define DN_MemListNewZ(arena, T) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewContiguous(arena, T, zmem) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), zmem) +#define DN_MemListNewContiguousZ(arena, T) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewArray(arena, T, count, zmem) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), zmem) +#define DN_MemListNewArrayZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewArrayNoZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) +#define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) +#define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) // NOTE: Arena // Overview @@ -4839,356 +5015,371 @@ DN_API void DN_MemListTempEnd DN_ArenaTempEnd(&temp); DN_ArenaDeinit(&arena); // Frees the memory */ -DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); -DN_API DN_Arena DN_ArenaFromHeap (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap); -DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); -DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); -DN_API void DN_ArenaTempEnd (DN_Arena *arena, DN_ArenaReset reset); -DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem); -DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_arena); -DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align); -DN_API void DN_ArenaDeinit (DN_Arena *arena); -DN_API bool DN_ArenaOwnsPtr (DN_Arena const *arena, void *ptr); +DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); +DN_API DN_Arena DN_ArenaFromHeapCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site); +#define DN_ArenaFromHeap(reserve, commit, flags, heap, name_cstr) DN_ArenaFromHeapCallSite(reserve, commit, flags, heap, DN_CallSiteNowNamed(name_cstr)) +DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); +DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); +DN_API void DN_ArenaTempEnd (DN_Arena *arena, DN_ArenaReset reset); +DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem); +DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_arena); +DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align); +DN_API void DN_ArenaDeinit (DN_Arena *arena); +DN_API bool DN_ArenaOwnsPtr (DN_Arena const *arena, void *ptr); -#define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) -#define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewContiguous(arena, T, zmem) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), zmem) -#define DN_ArenaNewContiguousZ(arena, T) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewArray(arena, T, count, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), zmem) -#define DN_ArenaNewArrayZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewArrayNoZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) -#define DN_ArenaNewCopy(arena, T, src) (T *)DN_ArenaCopy(arena, (src), sizeof(T), alignof(T)) -#define DN_ArenaNewArrayCopy(arena, T, src, count) (T *)DN_ArenaCopy(arena, (src), sizeof(T) * (count), alignof(T)) +#define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) +#define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewContiguous(arena, T, zmem) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), zmem) +#define DN_ArenaNewContiguousZ(arena, T) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewArray(arena, T, count, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), zmem) +#define DN_ArenaNewArrayZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewArrayNoZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) +#define DN_ArenaNewCopy(arena, T, src) (T *)DN_ArenaCopy(arena, (src), sizeof(T), alignof(T)) +#define DN_ArenaNewArrayCopy(arena, T, src, count) (T *)DN_ArenaCopy(arena, (src), sizeof(T) * (count), alignof(T)) -DN_API DN_Pool DN_PoolFromArena (DN_Arena *arena, DN_U8 align); -DN_API bool DN_PoolIsValid (DN_Pool const *pool); -DN_API void * DN_PoolAlloc (DN_Pool *pool, DN_USize size); -DN_API void DN_PoolDealloc (DN_Pool *pool, void *ptr); -DN_API void * DN_PoolCopy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align); -#define DN_PoolNew(pool, T) (T *)DN_PoolAlloc(pool, sizeof(T)) -#define DN_PoolNewArray(pool, T, count) (T *)DN_PoolAlloc(pool, count * sizeof(T)) -#define DN_PoolNewCopy(pool, T, src) (T *)DN_PoolCopy (pool, (src), sizeof(T), alignof(T)) -#define DN_PoolNewArrayCopy(pool, T, src, count) (T *)DN_PoolCopy (pool, (src), sizeof(T) * (count), alignof(T)) +DN_API DN_Pool DN_PoolFromArena (DN_Arena *arena, DN_U8 align); +DN_API bool DN_PoolIsValid (DN_Pool const *pool); +DN_API void * DN_PoolAlloc (DN_Pool *pool, DN_USize size); +DN_API void DN_PoolDealloc (DN_Pool *pool, void *ptr); +DN_API void * DN_PoolCopy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align); +#define DN_PoolNew(pool, T) (T *)DN_PoolAlloc(pool, sizeof(T)) +#define DN_PoolNewArray(pool, T, count) (T *)DN_PoolAlloc(pool, count * sizeof(T)) +#define DN_PoolNewCopy(pool, T, src) (T *)DN_PoolCopy (pool, (src), sizeof(T), alignof(T)) +#define DN_PoolNewArrayCopy(pool, T, src, count) (T *)DN_PoolCopy (pool, (src), sizeof(T) * (count), alignof(T)) -DN_API DN_ErrSink* DN_ErrSinkBegin_ (DN_ErrSink *err, DN_ErrSinkMode mode, DN_CallSite call_site); -#define DN_ErrSinkBegin(err, mode) DN_ErrSinkBegin_(err, mode, DN_CallSiteNow) -#define DN_ErrSinkBeginDefault(err) DN_ErrSinkBegin(err, DN_ErrSinkMode_Nil) -DN_API bool DN_ErrSinkHasError (DN_ErrSink *err); -DN_API DN_ErrSinkMsg* DN_ErrSinkEnd (DN_Arena *arena, DN_ErrSink *err); -DN_API DN_Str8 DN_ErrSinkEndStr8 (DN_Arena *arena, DN_ErrSink *err); -DN_API void DN_ErrSinkEndIgnore (DN_ErrSink *err); -DN_API bool DN_ErrSinkEndLogError_ (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg); -#define DN_ErrSinkEndLogError(err, err_msg) DN_ErrSinkEndLogError_(err, DN_CallSiteNow, err_msg) -DN_API bool DN_ErrSinkEndLogErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkEndLogErrorFV(err, fmt, args) DN_ErrSinkEndLogErrorFV_(err, DN_CallSiteNow, fmt, args) -DN_API bool DN_ErrSinkEndLogErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkEndLogErrorF(err, fmt, ...) DN_ErrSinkEndLogErrorF_(err, DN_CallSiteNow, fmt, ##__VA_ARGS__) -DN_API void DN_ErrSinkEndExitIfErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkEndExitIfErrorF(err, exit_val, fmt, ...) DN_ErrSinkEndExitIfErrorF_(err, DN_CallSiteNow, exit_val, fmt, ##__VA_ARGS__) -DN_API void DN_ErrSinkEndExitIfErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkEndExitIfErrorFV(err, exit_val, fmt, args) DN_ErrSinkEndExitIfErrorFV_(err, DN_CallSiteNow, exit_val, fmt, args) -DN_API void DN_ErrSinkAppendFV_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkAppendFV(error, error_code, fmt, args) DN_ErrSinkAppendFV_(error, error_code, DN_CallSiteNow, fmt, args) -DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CallSiteNow, fmt, ##__VA_ARGS__) +DN_API DN_ErrSink* DN_ErrSinkBegin_ (DN_ErrSink *err, DN_ErrSinkMode mode, DN_CallSite call_site); +#define DN_ErrSinkBegin(err, mode) DN_ErrSinkBegin_(err, mode, DN_CallSiteNow) +#define DN_ErrSinkBeginDefault(err) DN_ErrSinkBegin(err, DN_ErrSinkMode_Nil) +DN_API bool DN_ErrSinkHasError (DN_ErrSink *err); +DN_API DN_ErrSinkMsg* DN_ErrSinkEnd (DN_Arena *arena, DN_ErrSink *err); +DN_API DN_Str8 DN_ErrSinkEndStr8 (DN_Arena *arena, DN_ErrSink *err); +DN_API void DN_ErrSinkEndIgnore (DN_ErrSink *err); +DN_API bool DN_ErrSinkEndLogError_ (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg); +#define DN_ErrSinkEndLogError(err, err_msg) DN_ErrSinkEndLogError_(err, DN_CallSiteNow, err_msg) +DN_API bool DN_ErrSinkEndLogErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkEndLogErrorFV(err, fmt, args) DN_ErrSinkEndLogErrorFV_(err, DN_CallSiteNow, fmt, args) +DN_API bool DN_ErrSinkEndLogErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkEndLogErrorF(err, fmt, ...) DN_ErrSinkEndLogErrorF_(err, DN_CallSiteNow, fmt, ##__VA_ARGS__) +DN_API void DN_ErrSinkEndExitIfErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkEndExitIfErrorF(err, exit_val, fmt, ...) DN_ErrSinkEndExitIfErrorF_(err, DN_CallSiteNow, exit_val, fmt, ##__VA_ARGS__) +DN_API void DN_ErrSinkEndExitIfErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkEndExitIfErrorFV(err, exit_val, fmt, args) DN_ErrSinkEndExitIfErrorFV_(err, DN_CallSiteNow, exit_val, fmt, args) +DN_API void DN_ErrSinkAppendFV_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkAppendFV(error, error_code, fmt, args) DN_ErrSinkAppendFV_(error, error_code, DN_CallSiteNow, fmt, args) +DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CallSiteNow, fmt, ##__VA_ARGS__) -DN_API DN_TcInitArgs DN_TcInitArgsDefault (); -DN_API void DN_TcInit (DN_TcCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); -DN_API void DN_TcInitFromHeap (DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap); -DN_API void DN_TcDeinit (DN_TcCore *tc, DN_TcDeinitArenas deinit_arenas); -DN_API void DN_TcEquip (DN_TcCore *tc); -DN_API DN_TcCore* DN_TcGet (); -DN_API DN_Arena* DN_TcMainArena (); -DN_API DN_Pool* DN_TcMainPool (); -DN_API DN_Arena DN_TcTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); -DN_API DN_Arena DN_TcTempArenaFromArena (DN_Arena **conflicts, DN_USize count); -DN_API DN_TcScratch DN_TcScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); -DN_API DN_TcScratch DN_TcScratchBeginArena (DN_Arena **conflicts, DN_USize count); -DN_API void DN_TcScratchEnd (DN_TcScratch *scratch); -DN_API void DN_TcSetFrameArena (DN_Arena *arena); -DN_API DN_Arena* DN_TcFrameArena (); -DN_API DN_ErrSink* DN_TcErrSink (); -#define DN_TcErrSinkBegin(mode) DN_ErrSinkBegin(DN_TcErrSink(), mode) -#define DN_TcErrSinkBeginDefault() DN_ErrSinkBeginDefault(DN_TcErrSink()) +DN_API DN_TcInitArgs DN_TcInitArgsDefault (); +DN_API void DN_TcInit (DN_TcCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); +DN_API void DN_TcInitFromHeap (DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap); +DN_API void DN_TcDeinit (DN_TcCore *tc, DN_TcDeinitArenas deinit_arenas); +DN_API void DN_TcEquip (DN_TcCore *tc); +DN_API DN_TcCore* DN_TcGet (); +DN_API DN_Arena* DN_TcMainArena (); +DN_API DN_Pool* DN_TcMainPool (); +DN_API DN_Arena DN_TcTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_Arena DN_TcTempArenaFromArena (DN_Arena **conflicts, DN_USize count); +DN_API DN_TcScratch DN_TcScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_TcScratch DN_TcScratchBeginArena (DN_Arena **conflicts, DN_USize count); +DN_API void DN_TcScratchEnd (DN_TcScratch *scratch); +DN_API void DN_TcSetFrameArena (DN_Arena *arena); +DN_API DN_Arena* DN_TcFrameArena (); +DN_API DN_ErrSink* DN_TcErrSink (); +#define DN_TcErrSinkBegin(mode) DN_ErrSinkBegin(DN_TcErrSink(), mode) +#define DN_TcErrSinkBeginDefault() DN_ErrSinkBeginDefault(DN_TcErrSink()) -DN_API bool DN_CharIsAlphabet (char ch); -DN_API bool DN_CharIsDigit (char ch); -DN_API bool DN_CharIsAlphaNum (char ch); -DN_API bool DN_CharIsWhitespace (char ch); -DN_API bool DN_CharIsHex (char ch); -DN_API char DN_CharToLower (char ch); -DN_API char DN_CharToUpper (char ch); +DN_API bool DN_CharIsAlphabet (char ch); +DN_API bool DN_CharIsDigit (char ch); +DN_API bool DN_CharIsAlphaNum (char ch); +DN_API bool DN_CharIsWhitespace (char ch); +DN_API bool DN_CharIsHex (char ch); +DN_API char DN_CharToLower (char ch); +DN_API char DN_CharToUpper (char ch); -DN_API DN_U64FromResult DN_U64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_U64FromResult DN_U64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_U64FromResult DN_U64FromStr8 (DN_Str8 string); -DN_API DN_U64FromResult DN_U64FromPtrDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); -DN_API DN_U64FromResult DN_U64FromPtr (void const *data, DN_USize size); -DN_API DN_U64 DN_U64FromPtrUnsafeDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); -DN_API DN_U64 DN_U64FromPtrUnsafe (void const *data, DN_USize size); -DN_API DN_U64FromResult DN_U64FromHexPtr (void const *hex, DN_USize hex_count); -DN_API DN_U64 DN_U64FromHexPtrUnsafe (void const *hex, DN_USize hex_count); -DN_API DN_U64FromResult DN_U64FromHexStr8 (DN_Str8 hex); -DN_API DN_U64 DN_U64FromHexStr8Unsafe (DN_Str8 hex); -DN_API DN_U64 DN_U64FromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get U64 stored in big-endian at the high bytes [24:32) -DN_API DN_U64FromResult DN_U64FromU8x32HiBE (DN_U8x32 const *val); // Checks [0:24) bytes aren't set before getting the U64 -DN_API DN_USize DN_USizeFromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get USize stored in big-endian at the high bytes [32 - sizeof USize:32) -DN_API DN_USizeFromResult DN_USizeFromU8x32HiBE (DN_U8x32 const *val); // Checks [0:sizeof USize) bytes aren't set before getting the U64 -DN_API DN_U32FromResult DN_U32FromHexStr8 (DN_Str8 hex); -DN_API DN_U32FromResult DN_U32FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_U32FromResult DN_U32FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_U32FromResult DN_U32FromPtr (void const *data, DN_USize size); -DN_API DN_I64FromResult DN_I64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_I64FromResult DN_I64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_I64FromResult DN_I64FromStr8 (DN_Str8 string); -DN_API DN_I64FromResult DN_I64FromPtr (void const *data, DN_USize size); -DN_API DN_I64 DN_I64FromPtrUnsafe (void const *data, DN_USize size); +DN_API DN_U64FromResult DN_U64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_U64FromResult DN_U64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_U64FromResult DN_U64FromStr8 (DN_Str8 string); +DN_API DN_U64FromResult DN_U64FromPtrDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); +DN_API DN_U64FromResult DN_U64FromPtr (void const *data, DN_USize size); +DN_API DN_U64 DN_U64FromPtrUnsafeDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); +DN_API DN_U64 DN_U64FromPtrUnsafe (void const *data, DN_USize size); +DN_API DN_U64FromResult DN_U64FromHexPtr (void const *hex, DN_USize hex_count); +DN_API DN_U64 DN_U64FromHexPtrUnsafe (void const *hex, DN_USize hex_count); +DN_API DN_U64FromResult DN_U64FromHexStr8 (DN_Str8 hex); +DN_API DN_U64 DN_U64FromHexStr8Unsafe (DN_Str8 hex); +DN_API DN_U64 DN_U64FromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get U64 stored in big-endian at the high bytes [24:32) +DN_API DN_U64FromResult DN_U64FromU8x32HiBE (DN_U8x32 const *val); // Checks [0:24) bytes aren't set before getting the U64 +DN_API DN_USize DN_USizeFromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get USize stored in big-endian at the high bytes [32 - sizeof USize:32) +DN_API DN_USizeFromResult DN_USizeFromU8x32HiBE (DN_U8x32 const *val); // Checks [0:sizeof USize) bytes aren't set before getting the U64 +DN_API DN_U32FromResult DN_U32FromHexStr8 (DN_Str8 hex); +DN_API DN_U32FromResult DN_U32FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_U32FromResult DN_U32FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_U32FromResult DN_U32FromPtr (void const *data, DN_USize size); +DN_API DN_I64FromResult DN_I64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_I64FromResult DN_I64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_I64FromResult DN_I64FromStr8 (DN_Str8 string); +DN_API DN_I64FromResult DN_I64FromPtr (void const *data, DN_USize size); +DN_API DN_I64 DN_I64FromPtrUnsafe (void const *data, DN_USize size); -DN_API bool DN_U8x32Eq (DN_U8x32 const *lhs, DN_U8x32 const *rhs); -DN_API DN_U8x32 DN_U8x32FromBytesLeftPadZ (DN_U8 const *ptr, DN_USize size); -DN_API DN_U8x32 DN_U8x32FromHexUnsafe (DN_Str8 hex_32b); -DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); -DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value +DN_API bool DN_U8x32Eq (DN_U8x32 const *lhs, DN_U8x32 const *rhs); +DN_API DN_U8x32 DN_U8x32FromBytesLeftPadZ (DN_U8 const *ptr, DN_USize size); +DN_API DN_U8x32 DN_U8x32FromHexUnsafe (DN_Str8 hex_32b); +DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); +DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value -DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); -DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); -DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); -DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize count, DN_U8 align, DN_ZMem z_mem); +DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); +DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); +DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); +DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize count, DN_U8 align, DN_ZMem z_mem); +#define DN_AllocatorNew(allocator, T, zmem) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), zmem) +#define DN_AllocatorNewZ(allocator, T) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_AllocatorNewNoZ(allocator, T) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), DN_ZMem_No) +#define DN_AllocatorNewArray(allocator, T, count, zmem) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), zmem) +#define DN_AllocatorNewArrayZ(allocator, T, count) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_AllocatorNewArrayNoZ(allocator, T, count) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), DN_ZMem_No) -DN_API DN_USize DN_FmtVCount (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_USize DN_FmtCount (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); -DN_API DN_FmtAppendResult DN_FmtAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...); -DN_API DN_FmtAppendResult DN_FmtAppendTruncate (char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...); -DN_API DN_USize DN_CStr8Count (char const *src); -DN_API DN_USize DN_CStr16Count (wchar_t const *src); +DN_API DN_USize DN_FmtVCount (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_USize DN_FmtCount (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); +DN_API DN_FmtAppendResult DN_FmtAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...); +DN_API DN_FmtAppendResult DN_FmtAppendTruncate (char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...); +DN_API DN_USize DN_CStr8Count (char const *src); +DN_API DN_USize DN_CStr16Count (wchar_t const *src); -#define DN_Str16Lit(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1} -#define DN_Str16FromPtr(data, count) DN_Literal(DN_Str16){(wchar_t *)(data), (DN_USize)(count)} +#define DN_Str16Lit(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1} +#define DN_Str16FromPtr(data, count) DN_Literal(DN_Str16){(wchar_t *)(data), (DN_USize)(count)} -#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} -#define DN_Str8PrintFmt(string) (int)((string).count), (string).data +#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} +#define DN_Str8PrintFmt(string) (int)((string).count), (string).data -#define DN_Str8FromPtr(data, count) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(count)} -#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->count) -#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) -DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize count, DN_ZMem z_mem, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8AllocArena (DN_USize count, DN_ZMem z_mem, DN_Arena *arena); -DN_API DN_Str8 DN_Str8AllocPool (DN_USize count, DN_Pool *pool); +#define DN_Str8FromPtr(data, count) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(count)} +#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->count) +#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) +DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize count, DN_ZMem z_mem, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8AllocArena (DN_USize count, DN_ZMem z_mem, DN_Arena *arena); +DN_API DN_Str8 DN_Str8AllocPool (DN_USize count, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); -DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize count, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize count, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); +DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize count, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize count, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x32 DN_Str8x32FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x64 DN_Str8x64FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x64 DN_Str8x64FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x128 DN_Str8x128FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x256 DN_Str8x256FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x512 DN_Str8x512FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x512 DN_Str8x512FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x1024 DN_Str8x1024FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x1024 DN_Str8x1024FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x16AppendFmt (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x16AppendFmtV (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x32AppendFmt (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x32AppendFmtV (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x64AppendFmt (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x64AppendFmtV (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x128AppendFmt (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x128AppendFmtV (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x256AppendFmt (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x256AppendFmtV (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x512AppendFmt (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x32 DN_Str8x32FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x64 DN_Str8x64FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x64 DN_Str8x64FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x128 DN_Str8x128FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x256 DN_Str8x256FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x512 DN_Str8x512FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x512 DN_Str8x512FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x1024 DN_Str8x1024FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x1024 DN_Str8x1024FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x16AppendFmt (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x16AppendFmtV (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x32AppendFmt (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x32AppendFmtV (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x64AppendFmt (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x64AppendFmtV (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x128AppendFmt (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x128AppendFmtV (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x256AppendFmt (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x256AppendFmtV (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x512AppendFmt (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char seperator); -DN_API bool DN_Str8Is (DN_Str8 string, DN_Str8IsFlags flags); -DN_API char * DN_Str8End (DN_Str8 string); -DN_API DN_Str8 DN_Str8Subset (DN_Str8 string, DN_USize offset, DN_USize count); -DN_API DN_Str8 DN_Str8Advance (DN_Str8 string, DN_USize amount); -DN_API DN_Str8 DN_Str8NextLine (DN_Str8 string); -DN_API DN_Str8BSplitResult DN_Str8BSplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); -DN_API DN_Str8BSplitResult DN_Str8BSplit (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); -DN_API DN_Str8BSplitResult DN_Str8BSplitLast (DN_Str8 string, DN_Str8 find); -DN_API DN_USize DN_Str8Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags mode); -DN_API DN_Str8SplitResult DN_Str8SplitArena (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags mode, DN_Arena *arena); -DN_API DN_Str8FindResult DN_Str8FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case); -DN_API DN_Str8FindResult DN_Str8FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case); -DN_API DN_Str8FindResult DN_Str8Find (DN_Str8 string, DN_Str8FindFlag flags); -DN_API DN_Str8 DN_Str8Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); -DN_API DN_Str8 DN_Str8ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); -DN_API bool DN_Str8Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API bool DN_Str8EqSensitive (DN_Str8 lhs, DN_Str8 rhs); -DN_API bool DN_Str8EqInsensitive (DN_Str8 lhs, DN_Str8 rhs); -DN_API bool DN_Str8StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API bool DN_Str8StartsWithSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8StartsWithInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API bool DN_Str8EndsWithSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); +DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char seperator); +DN_API bool DN_Str8Is (DN_Str8 string, DN_Str8IsFlags flags); +DN_API char * DN_Str8End (DN_Str8 string); +DN_API DN_Str8 DN_Str8Subset (DN_Str8 string, DN_USize offset, DN_USize count); +DN_API DN_Str8 DN_Str8Advance (DN_Str8 string, DN_USize amount); +DN_API DN_Str8 DN_Str8NextLine (DN_Str8 string); +DN_API DN_Str8BSplitResult DN_Str8BSplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); +DN_API DN_Str8BSplitResult DN_Str8BSplit (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); +DN_API DN_Str8BSplitResult DN_Str8BSplitLast (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags mode); +DN_API DN_Str8SplitResult DN_Str8SplitArena (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags mode, DN_Arena *arena); +DN_API DN_Str8FindResult DN_Str8FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case); +DN_API DN_Str8FindResult DN_Str8FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case); +DN_API DN_Str8FindResult DN_Str8Find (DN_Str8 string, DN_Str8FindFlag flags); +DN_API DN_Str8 DN_Str8Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); +DN_API DN_Str8 DN_Str8ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); +DN_API bool DN_Str8Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API bool DN_Str8EqSensitive (DN_Str8 lhs, DN_Str8 rhs); +DN_API bool DN_Str8EqInsensitive (DN_Str8 lhs, DN_Str8 rhs); +DN_API bool DN_Str8StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API bool DN_Str8StartsWithSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8StartsWithInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API bool DN_Str8EndsWithSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); -DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimPrefixSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API DN_Str8 DN_Str8TrimPrefixInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimSuffixSensitive (DN_Str8 string, DN_Str8 suffix); -DN_API DN_Str8 DN_Str8TrimSuffixInsensitive (DN_Str8 string, DN_Str8 suffix); -DN_API DN_Str8 DN_Str8TrimAround (DN_Str8 string, DN_Str8 trim_string, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimAroundSensitive (DN_Str8 string, DN_Str8 trim_string); -DN_API DN_Str8 DN_Str8TrimAroundInsensitive (DN_Str8 string, DN_Str8 trim_string); -DN_API DN_Str8 DN_Str8TrimHeadWhitespace (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimPrefixSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API DN_Str8 DN_Str8TrimPrefixInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimSuffixSensitive (DN_Str8 string, DN_Str8 suffix); +DN_API DN_Str8 DN_Str8TrimSuffixInsensitive (DN_Str8 string, DN_Str8 suffix); +DN_API DN_Str8 DN_Str8TrimAround (DN_Str8 string, DN_Str8 trim_string, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimAroundSensitive (DN_Str8 string, DN_Str8 trim_string); +DN_API DN_Str8 DN_Str8TrimAroundInsensitive (DN_Str8 string, DN_Str8 trim_string); +DN_API DN_Str8 DN_Str8TrimHeadWhitespace (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); -DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileDirectoryFromPath (DN_Str8 path); -DN_API DN_Str8 DN_Str8AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...); -DN_API DN_Str8 DN_Str8AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...); -DN_API DN_Str8 DN_Str8FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args); -DN_API void DN_Str8Remove (DN_Str8 *string, DN_USize offset, DN_USize count); -DN_API DN_Str8 DN_Str8TruncateArena (DN_Str8 string, DN_USize max_count, DN_Str8 truncator, DN_Arena *arena); -DN_API DN_Str8TruncResult DN_Str8TruncMiddlePtr (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, char *dest, DN_USize dest_max); -DN_API DN_Str8TruncResult DN_Str8TruncMiddle (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Lower (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Upper (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileDirectoryFromPath (DN_Str8 path); +DN_API DN_Str8 DN_Str8AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...); +DN_API DN_Str8 DN_Str8AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...); +DN_API DN_Str8 DN_Str8FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args); +DN_API void DN_Str8Remove (DN_Str8 *string, DN_USize offset, DN_USize count); +DN_API DN_Str8 DN_Str8TruncateArena (DN_Str8 string, DN_USize max_count, DN_Str8 truncator, DN_Arena *arena); +DN_API DN_Str8TruncResult DN_Str8TruncMiddlePtr (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, char *dest, DN_USize dest_max); +DN_API DN_Str8TruncResult DN_Str8TruncMiddle (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Lower (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Upper (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); +DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); +DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); #if DN_WITH_STR8_AVX512F -DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); -DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); #endif -DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 seperator, DN_Arena *arena); -DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); -DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 seperator, DN_Arena *arena); +DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); +DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); -DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 seperator, DN_Arena *arena); -DN_API DN_Str16 DN_Str16RenderSpaceSep (DN_Str16Slice array, DN_Arena *arena); +DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); +DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 seperator, DN_Arena *arena); +DN_API DN_Str16 DN_Str16RenderSpaceSep (DN_Str16Slice array, DN_Arena *arena); -DN_API DN_Str8Builder DN_Str8BuilderFromArena (DN_Arena *arena); -DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); -DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); -DN_API DN_Str8Builder DN_Str8BuilderFromBuilder (DN_Arena *arena, DN_Str8Builder const *builder); -DN_API bool DN_Str8BuilderAddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_Str8BuilderAdd add); -DN_API bool DN_Str8BuilderAddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_Str8BuilderAdd add); -DN_API bool DN_Str8BuilderAddFV (DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_Str8BuilderAppendArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Append) -#define DN_Str8BuilderAppendArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Append) -#define DN_Str8BuilderAppendSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_Str8BuilderAdd_Append) -#define DN_Str8BuilderAppendSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_Str8BuilderAdd_Append) -DN_API bool DN_Str8BuilderAppendRef (DN_Str8Builder *builder, DN_Str8 string); -DN_API bool DN_Str8BuilderAppendCopy (DN_Str8Builder *builder, DN_Str8 string); -#define DN_Str8BuilderAppendFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Append, fmt, args) -DN_API bool DN_Str8BuilderAppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_Str8BuilderAppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize count); -DN_API bool DN_Str8BuilderAppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize count); -DN_API bool DN_Str8BuilderAppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src); -DN_API bool DN_Str8BuilderAppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src); -#define DN_Str8BuilderPrependArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Prepend) -#define DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Prepend) -#define DN_Str8BuilderPrependSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_Str8BuilderAdd_Prepend) -#define DN_Str8BuilderPrependSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_Str8BuilderAdd_Prepend) -DN_API bool DN_Str8BuilderPrependRef (DN_Str8Builder *builder, DN_Str8 string); -DN_API bool DN_Str8BuilderPrependCopy (DN_Str8Builder *builder, DN_Str8 string); -#define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Prepend, fmt, args) -DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); -DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); +DN_API DN_Str8Builder DN_Str8BuilderFromArena (DN_Arena *arena); +DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); +DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); +DN_API DN_Str8Builder DN_Str8BuilderFromBuilder (DN_Arena *arena, DN_Str8Builder const *builder); +DN_API bool DN_Str8BuilderAddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); +DN_API bool DN_Str8BuilderAddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); +DN_API bool DN_Str8BuilderAddFV (DN_Str8Builder *builder, DN_AddType add, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_Str8BuilderAppendArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Append) +#define DN_Str8BuilderAppendArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Append) +#define DN_Str8BuilderAppendSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Append) +#define DN_Str8BuilderAppendSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Append) +DN_API bool DN_Str8BuilderAppendRef (DN_Str8Builder *builder, DN_Str8 string); +DN_API bool DN_Str8BuilderAppendCopy (DN_Str8Builder *builder, DN_Str8 string); +#define DN_Str8BuilderAppendFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Append, fmt, args) +DN_API bool DN_Str8BuilderAppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_Str8BuilderAppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize count); +DN_API bool DN_Str8BuilderAppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize count); +DN_API bool DN_Str8BuilderAppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src); +DN_API bool DN_Str8BuilderAppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src); +#define DN_Str8BuilderPrependArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Prepend) +#define DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Prepend) +#define DN_Str8BuilderPrependSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Prepend) +#define DN_Str8BuilderPrependSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Prepend) +DN_API bool DN_Str8BuilderPrependRef (DN_Str8Builder *builder, DN_Str8 string); +DN_API bool DN_Str8BuilderPrependCopy (DN_Str8Builder *builder, DN_Str8 string); +#define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Prepend, fmt, args) +DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); -DN_API bool DN_PathAddRef (DN_Path *path, DN_Str8 add, DN_Arena *arena); -DN_API bool DN_PathAdd (DN_Path *path, DN_Str8 add, DN_Arena *arena); -DN_API bool DN_PathAddF (DN_Path *path, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_PathPop (DN_Path *path); -DN_API DN_Str8 DN_Str8FromPath (DN_Path const *path, DN_Str8 path_seperator, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FmtPathVArena (DN_Str8 path_seperator, DN_Arena *arena, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPathArena (DN_Str8 path_seperator, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtPathVPool (DN_Str8 path_seperator, DN_Pool *pool, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPathPool (DN_Str8 path_seperator, DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtOsPathArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtOsPathPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_PathAddRef (DN_Path *path, DN_Str8 add, DN_Arena *arena); +DN_API bool DN_PathAdd (DN_Path *path, DN_Str8 add, DN_Arena *arena); +DN_API bool DN_PathAddF (DN_Path *path, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_PathPop (DN_Path *path); +DN_API DN_Str8 DN_Str8FromPath (DN_Path const *path, DN_Str8 path_seperator, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FmtPathVArena (DN_Str8 path_seperator, DN_Arena *arena, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPathArena (DN_Str8 path_seperator, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtPathVPool (DN_Str8 path_seperator, DN_Pool *pool, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPathPool (DN_Str8 path_seperator, DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtOsPathArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtOsPathPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API int DN_UTF8Encode (DN_U8 utf8[4], DN_U32 codepoint); -DN_API int DN_UTF16Encode (DN_U16 utf16[2], DN_U32 codepoint); -DN_API DN_UTF8DecodeResult DN_UTF8Decode (DN_Str8 stream); -DN_API bool DN_UTF8DecodeIterate (DN_UTF8DecodeIterator *it, DN_Str8 utf8); -DN_API DN_USize DN_USizeCodepointCountFromUTF8 (DN_Str8 str, DN_CodepointCountFlags flags); +DN_API DN_USize DN_Utf8FromUtf32 (DN_U8 utf8[4], DN_U32 codepoint); +DN_API bool DN_Utf32IsAlphabet (DN_U32 codepoint); +DN_API bool DN_Utf32IsDigit (DN_U32 codepoint); +DN_API bool DN_Utf32IsAlphaNum (DN_U32 codepoint); +DN_API bool DN_Utf32IsWhitespace (DN_U32 codepoint); +DN_API bool DN_Utf32IsHex (DN_U32 codepoint); +DN_API bool DN_Utf32IsWordCharacter (DN_U32 codepoint); +DN_API DN_U32 DN_Utf32ToLower (DN_U32 codepoint); +DN_API DN_U32 DN_Utf32ToUpper (DN_U32 codepoint); +DN_API DN_USize DN_Utf32FromUtf16 (DN_U16 utf16[2], DN_U32 codepoint); +DN_API DN_Utf32FromResult DN_Utf32FromUtf8Stream (DN_Str8 stream); +DN_API bool DN_Utf32FromUtf8Iterate (DN_Utf32FromUtf8Iterator *it, DN_Utf32IterateDir dir, DN_Str8 utf8); +DN_API DN_USize DN_Utf32CodepointCountFromUtf8 (DN_Str8 str, DN_CodepointCountFlags flags); -DN_API DN_U8 DN_U8FromHexNibble (char hex); -DN_API DN_NibbleFromU8Result DN_NibbleFromU8 (DN_U8 u8); +DN_API DN_U8 DN_U8FromHexNibble (char hex); +DN_API DN_NibbleFromU8Result DN_NibbleFromU8 (DN_U8 u8); -DN_API DN_USize DN_PtrBytesFromStr8Hex (DN_Str8 hex, void *dest, DN_USize dest_count); -DN_API DN_USize DN_PtrBytesFromPtrHex (char const *hex, DN_USize hex_count, void *dest, DN_USize dest_count); -DN_API DN_Str8 DN_Str8BytesFromStr8HexArena (DN_Str8 hex, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BytesFromPtrHexArena (char const *hex, DN_USize hex_count, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BytesFromPtrHexPool (char const *hex, DN_USize hex_count, DN_Pool *pool); -DN_API DN_U8x16 DN_U8x16FromPtrHex32 (char const *hex, DN_USize hex_count); -DN_API DN_U8x32 DN_U8x32FromPtrHex64 (char const *hex, DN_USize hex_count); +DN_API DN_USize DN_PtrBytesFromStr8Hex (DN_Str8 hex, void *dest, DN_USize dest_count); +DN_API DN_USize DN_PtrBytesFromPtrHex (char const *hex, DN_USize hex_count, void *dest, DN_USize dest_count); +DN_API DN_Str8 DN_Str8BytesFromStr8HexArena (DN_Str8 hex, DN_Arena *arena); +DN_API DN_Str8 DN_Str8BytesFromPtrHexArena (char const *hex, DN_USize hex_count, DN_Arena *arena); +DN_API DN_Str8 DN_Str8BytesFromPtrHexPool (char const *hex, DN_USize hex_count, DN_Pool *pool); +DN_API DN_U8x16 DN_U8x16FromPtrHex32 (char const *hex, DN_USize hex_count); +DN_API DN_U8x32 DN_U8x32FromPtrHex64 (char const *hex, DN_USize hex_count); -DN_API DN_HexU64 DN_HexU64FromU64 (DN_U64 value, DN_HexFromU64Type type); -DN_API DN_USize DN_PtrHexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_USize DN_PtrHexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8 DN_Str8HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8 DN_Str8HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex32 DN_Hex32FromPtrBytes16 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex64 DN_Hex64FromPtrBytes32 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex64 DN_Hex64FromU8x32 (DN_U8x32 const *value, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex128 DN_Hex128FromPtrBytes64 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_HexU64 DN_HexU64FromU64 (DN_U64 value, DN_HexFromU64Type type); +DN_API DN_USize DN_PtrHexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_USize DN_PtrHexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_Str8HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_Str8HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex32 DN_Hex32FromPtrBytes16 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex64 DN_Hex64FromPtrBytes32 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex64 DN_Hex64FromU8x32 (DN_U8x32 const *value, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex128 DN_Hex128FromPtrBytes64 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8x128 DN_AgeStr8FromMsU64 (DN_U64 duration_ms, DN_AgeUnit units); -DN_API DN_Str8x128 DN_AgeStr8FromSecU64 (DN_U64 duration_ms, DN_AgeUnit units); -DN_API DN_Str8x128 DN_AgeStr8FromSecF64 (DN_F64 sec, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromMsU64 (DN_U64 duration_ms, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromSecU64 (DN_U64 duration_ms, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromSecF64 (DN_F64 sec, DN_AgeUnit units); -DN_API int DN_IsLeapYear (int year); -DN_API bool DN_DateIsValid (DN_Date date); -DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); -DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); +DN_API int DN_IsLeapYear (int year); +DN_API bool DN_DateIsValid (DN_Date date); +DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); +DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); -DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); -DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); -DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); -#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) +DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); +DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); +#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) // NOTE: Profiler // Overview @@ -5252,6 +5443,17 @@ DN_API void DN_ProfilerFmtToStdout DN_API DN_F64 DN_ProfilerSecFromTsc (DN_Profiler *profiler, DN_U64 duration_tsc); DN_API DN_F64 DN_ProfilerMsFromTsc (DN_Profiler *profiler, DN_U64 duration_tsc); +DN_API DN_Profiler2 DN_Profiler2Init (DN_ProfilerTscNowFunc *tsc_now_func, DN_U64 tsc_frequency, DN_Profiler2Marker *markers, DN_U32 markers_count, DN_Profiler2Frame *frames, DN_U32 frames_count); +DN_API void DN_Profiler2FrameBegin (DN_Profiler2 *profiler); +DN_API DN_Profiler2Zone DN_Profiler2ZoneBegin (DN_Profiler2 *profiler, DN_Str8 name, DN_CallSite call_site); +DN_API void DN_Profiler2ZoneEnd (DN_Profiler2Zone *zone); +#define DN_Profiler2ZoneBeginName(profiler, name_cstr) DN_Profiler2ZoneBegin(profiler, DN_Str8Lit(name_cstr), DN_CallSiteNow) +#define DN_Profiler2ZoneBeginUnnamed(profiler) DN_Profiler2ZoneBegin(profiler, DN_Str8Lit(""), DN_CallSiteNow) +#define DN_Profiler2ZoneScopeName(profiler, name_cstr) DN_Profiler2Zone DN_UniqueName(zone) = DN_Profiler2ZoneBeginName(profiler, name_cstr); !DN_UniqueName(zone).scope_once; DN_Profiler2ZoneEnd(DN_UniqueName(&zone)) +DN_API bool DN_Profiler2MarkerWalkDfsPreOrder (DN_Profiler2 *profiler, DN_Profiler2Iterator *it); +DN_API DN_V2USize DN_Profiler2MarkerOffsetFromFrame (DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame); +DN_API DN_Profiler2Marker* DN_Profiler2MarkerFromFrameIndex (DN_Profiler2 *profiler, DN_Profiler2Frame *frame, DN_U32 index); + DN_API void DN_QSort_ (void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare); #if defined(__cplusplus) template void DN_QSortT (T *array, DN_USize array_size, void *user_context, DN_QSortCompareFunc *compare); @@ -5299,16 +5501,12 @@ DN_API void DN_Pcg32Advance DN_API DN_U32 DN_Fnv1aHashU32FromBytes (void const *bytes, DN_USize count, DN_U32 seed); DN_API DN_U64 DN_Fnv1aHashU64FromBytes (void const *bytes, DN_USize count, DN_U64 seed); -DN_API DN_U32 DN_Murmur3HashU32FromBytesX86 (void const *bytes, int len, DN_U32 seed); -DN_API DN_Murmur3 DN_Murmur3HashU128FromBytesX64 (void const *bytes, int len, DN_U32 seed); -DN_API DN_U64 DN_Murmur3HashU64FromBytesX64 (void const *bytes, int len, DN_U32 seed); -DN_API DN_U32 DN_Murmur3HashU32FromBytesX64 (void const *bytes, int len, DN_U32 seed); - -#if defined(DN_64_BIT) - #define DN_Murmur3HashU32FromBytes(bytes, len, seed) DN_Murmur3HashU32FromBytesX64(bytes, len, seed) -#else - #define DN_Murmur3HashU32FromBytes(bytes, len, seed) DN_Murmur3HashU32FromBytesX86(bytes, len, seed) -#endif +DN_API void DN_Murmur3HashRawProcess (DN_U32 *ph1, DN_U32 *pcarry, const void *key, int len); +DN_API DN_U32 DN_Murmur3HashRawResult (DN_U32 h1, DN_U32 carry, DN_U32 total_length); +DN_API DN_U32 DN_Murmur3Hash (DN_U32 seed, const void *key, int len); +DN_API DN_Murmur3Context DN_Murmur3HashInit (DN_U32 seed); +DN_API void DN_Murmur3HashUpdate (DN_Murmur3Context *ctx, const void *key, int len); +DN_API DN_U32 DN_Murmur3HashFinish (DN_Murmur3Context const *ctx); #define DN_AnsiCodeBoldLit "\x1b[1m" #define DN_AnsiCodeResetLit "\x1b[0m" @@ -5559,17 +5757,17 @@ DN_API DN_V3F32 DN_V3F32Normalise #define DN_V4F32From1N(x) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}} #define DN_V4F32From4N(x, y, z, w) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}} -#define DN_V4F32FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{xyz.x, xyz.y, xyz.z, w}} -#define DN_V4F32Rgba01FromRgbaU8(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}} -#define DN_V4F32Rgba01FromRgbU8(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}} +#define DN_V4F32FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{(xyz).x, (xyz).y, (xyz).z, (w)}} +#define DN_V4F32FromRgba255ToRgba01(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}} +#define DN_V4F32FromRgb255ToRgba01(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}} DN_API DN_V4F32 DN_V4F32Lerp (DN_V4F32 lhs, DN_F32 t01, DN_V4F32 rhs); DN_API bool DN_V4F32Rgba01IsValid (DN_V4F32 rgba01); -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbU32 (DN_U32 u32); -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbaU32 (DN_U32 u32); -DN_API DN_V4F32 DN_V4F32Linear01FromSrgb01 (DN_V4F32 rgb01); +DN_API DN_V4F32 DN_V4F32FromRgbU32ToRgba01 (DN_U32 u32); +DN_API DN_V4F32 DN_V4F32FromRgbaU32ToRgba01 (DN_U32 u32); +DN_API DN_V4F32 DN_V4F32FromSrgb01ToLinear01 (DN_V4F32 rgb01); DN_API DN_V4F32 DN_V4F32Linear01Desaturate (DN_V4F32 linear01, DN_F32 t01); -DN_API DN_V4F32 DN_V4F32Srgb01FromLinear01 (DN_V4F32 linear01); +DN_API DN_V4F32 DN_V4F32FromLinear01ToSrgb01 (DN_V4F32 linear01); #define DN_V4F32FromV4Alpha(v4, alpha) DN_V4F32FromV3And1N(v4.xyz, alpha) @@ -5614,6 +5812,7 @@ DN_API DN_Str8x256 DN_M4ColumnMajorString DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API DN_M2x3 DN_M2x3Identity (); +DN_API bool DN_M2x3IsIdentity (DN_M2x3 m2x3); DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset); DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3); DN_API DN_M2x3 DN_M2x3Scale (DN_V2F32 scale); @@ -5669,50 +5868,175 @@ DN_API DN_Rect DN_RectCutCut DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 (DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b); -// NOTE: Containers that are imlpemented using primarily macros for operating on data structures +// NOTE: Containers that are implemented using primarily macros for operating on data structures // that are embedded into a C style struct or from a set of defined variables from the available // scope. Keep it stupid simple, structs and functions. Minimal amount of container types with // flexible construction leads to less duplicated container code and less template meta-programming. -// NOTE: Intrusive Singly Linked List -// Define a struct with the members `next`: +// NOTE: Lists (Linked, Queue, Stack) // -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// } my_link = {}; -// -// MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem); -#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) +// 1. Fully double-linked with first and last pointers (linked list) +// 2. Singly linked with first and last pointers from the list head (queue list) +// 3. Singly linked with just a single first pointer from the header (stack list) -// NOTE: Singly Linked List with Head and Tail pointer -/* - struct MyLinkItem { - int data; - MyLinkItem *next; - } my_list = {}; +#if defined(NDEBUG) + #define DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, check_ptr) +#else + #define DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, check_ptr) \ + bool DN_UniqueName(dbg_verify_pointer_in_list) = false; \ + for (Type *dbg_it_ = first_ptr; dbg_it_; dbg_it_ = dbg_it_->field_for_next) { \ + if (dbg_it_ == check_ptr) { \ + DN_UniqueName(dbg_verify_pointer_in_list) = true; \ + break; \ + } \ + } \ + DN_AssertF(DN_UniqueName(dbg_verify_pointer_in_list), "Pointer %p is not a part of the linked list at %p", check_ptr, first_ptr); +#endif - struct MyContainer { - MyLinkItem *head; - MyLinkItem *tail; - }; +#define DN_ForList(Type, var_it_name, first_ptr, field_for_next) \ + Type *var_it_name = (first_ptr), *var_it_name##_next = nullptr; (var_it_name ? (var_it_name##_next = var_it_name->field_for_next, 1) : 0); var_it_name = var_it_name##_next - MyLinkItem item = {}; - MyContainer container = {}; - DN_ISinglyHeadTailLLAppend(container, item); - // ... or alternatively, DN_SinglyHeadTailLLAppend(container.head, container.tail, item); - - for (MyLinkItem *it = container.head; it; it = it->next) { } -*/ -#define DN_SinglyHeadTailLLAppend(head, tail, to_append) \ - do { \ - if (!head) \ - head = to_append; \ - if (tail) \ - tail->next = to_append; \ - tail = to_append; \ +#define DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, add) \ + do { \ + if (first_ptr) { \ + DN_AssertF(last_ptr, "Invalid list: first is set (%p) but last is null", first_ptr); \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, last_ptr); \ + } else { \ + DN_AssertF(!last_ptr, "Invalid list: first is null but last is set (%p)"); \ + } \ + DN_AssertF(!item_to_add_ptr->field_for_next, "Invalid list: pointer to add already has next set (%p), should be null", item_to_add_ptr->field_for_next); \ + if (add == DN_AddType_Append) { \ + if (!first_ptr) \ + first_ptr = item_to_add_ptr; \ + if (last_ptr) \ + last_ptr->field_for_next = item_to_add_ptr; \ + last_ptr = item_to_add_ptr; \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + item_to_add_ptr->field_for_next = first_ptr; \ + first_ptr = item_to_add_ptr; \ + if (!last_ptr) \ + last_ptr = item_to_add_ptr; \ + } \ } while (0) -#define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append) + +#define DN_ListQueueAppend(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr) DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, DN_AddType_Append) +#define DN_ListQueuePrepend(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr) DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, DN_AddType_Prepend) + +#define DN_ListQueueDetach(Type, first_ptr, last_ptr, field_for_next, detach_ptr) \ + do { \ + if ((detach_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_ptr); \ + if ((detach_ptr) == (first_ptr)) { \ + if ((first_ptr) == (last_ptr)) \ + (last_ptr) = nullptr; \ + (first_ptr) = (detach_ptr)->field_for_next; \ + } else { \ + Type *prev_ = (first_ptr); \ + while (prev_ && prev_->field_for_next != (detach_ptr)) \ + prev_ = prev_->field_for_next; \ + prev_->field_for_next = (detach_ptr)->field_for_next; \ + if ((detach_ptr) == (last_ptr)) \ + (last_ptr) = prev_; \ + } \ + (detach_ptr)->field_for_next = nullptr; \ + } \ + } while (0) + +#define DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, add) \ + do { \ + if ((first_ptr)) { \ + DN_AssertF((last_ptr), "Invalid list: first is set (%p) but last is null", (first_ptr)); \ + } else { \ + DN_AssertF(!(last_ptr), "Invalid list: first is null but last is set (%p)", (last_ptr)); \ + (first_ptr) = (dest_ptr); \ + (last_ptr) = (dest_ptr); \ + DN_AssertF(!(dest_ptr)->field_for_next, "Invalid list: First and last is being initialised to dest (%p) but dest has next (%p) set, dest cannot be assigned as the last pointer", (dest_ptr), (dest_ptr)->field_for_next); \ + DN_AssertF(!(dest_ptr)->field_for_prev, "Invalid list: First and last is being initialised to dest (%p) but dest has prev (%p) set, dest cannot be assigned as the first pointer", (dest_ptr), (dest_ptr)->field_for_prev); \ + } \ + if ((src_ptr)) { \ + if (add == DN_AddType_Append) { \ + (src_ptr)->field_for_next = (dest_ptr) ? (dest_ptr)->field_for_next : nullptr; \ + (src_ptr)->field_for_prev = (dest_ptr); \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + (src_ptr)->field_for_next = (dest_ptr); \ + (src_ptr)->field_for_prev = (dest_ptr) ? (dest_ptr)->field_for_prev : nullptr; \ + } \ + } \ + if ((dest_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, dest_ptr); \ + if (add == DN_AddType_Append) { \ + if ((dest_ptr)->field_for_next) \ + (dest_ptr)->field_for_next->field_for_prev = (src_ptr); \ + (dest_ptr)->field_for_next = (src_ptr); \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + if ((dest_ptr)->field_for_prev) \ + (dest_ptr)->field_for_prev->field_for_next = (src_ptr); \ + (dest_ptr)->field_for_prev = (src_ptr); \ + } \ + } \ + if ((dest_ptr) == (first_ptr) && add == DN_AddType_Prepend) \ + (first_ptr) = (src_ptr); \ + if ((dest_ptr) == (last_ptr) && add == DN_AddType_Append) \ + (last_ptr) = (src_ptr); \ + } while (0) + +#define DN_ListLinkedAppendTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, DN_AddType_Append) +#define DN_ListLinkedPrependTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, DN_AddType_Prepend) +#define DN_ListLinkedAppend(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, last_ptr, DN_AddType_Append) +#define DN_ListLinkedPrepend(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, first_ptr, DN_AddType_Prepend) +#define DN_ListLinkedDetach(Type, first_ptr, last_ptr, field_for_next, field_for_prev, detach_ptr) \ + do { \ + if ((first_ptr) && (last_ptr) && (detach_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_ptr); \ + if ((detach_ptr) == (first_ptr)) \ + (first_ptr) = (detach_ptr)->field_for_next; \ + if ((detach_ptr) == (last_ptr)) \ + (last_ptr) = (detach_ptr)->field_for_prev; \ + if ((detach_ptr)->field_for_prev) \ + (detach_ptr)->field_for_prev->field_for_next = (detach_ptr)->field_for_next; \ + if ((detach_ptr)->field_for_next) \ + (detach_ptr)->field_for_next->field_for_prev = (detach_ptr)->field_for_prev; \ + (detach_ptr)->field_for_next = nullptr; \ + (detach_ptr)->field_for_prev = nullptr; \ + } \ + } while (0) + +#define DN_ListStackPush(Type, first_ptr, field_for_next, item_ptr) \ + do { \ + DN_AssertF(!(item_ptr)->field_for_next, \ + "Invalid list: pointer to push already has next set (%p), should be null", \ + (void *)(item_ptr)->field_for_next); \ + (item_ptr)->field_for_next = (first_ptr); \ + (first_ptr) = (item_ptr); \ + } while (0) + +#define DN_ListStackDetach(Type, first_ptr, field_for_next, detach_ptr) \ + do { \ + /* NOTE: `detach_` caches `detach_ptr` into a local because `detach_ptr` can alias `first_ptr`. \ + DN_ListStackPop() passes `first_ptr` as both arguments and mutates `first_ptr` which would \ + also modify `detach_ptr` causing unwanted side-effects like a null-ptr deref. */ \ + Type *detach_ = (detach_ptr); \ + if ((first_ptr) && (detach_)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_); \ + if (detach_ == (first_ptr)) { \ + (first_ptr) = detach_->field_for_next; \ + } else { \ + Type *prev_ = (first_ptr); \ + while (prev_ && prev_->field_for_next != detach_) \ + prev_ = prev_->field_for_next; \ + DN_Assert(prev_); \ + if (prev_) \ + prev_->field_for_next = detach_->field_for_next; \ + } \ + detach_->field_for_next = nullptr; \ + } \ + } while (0) + +#define DN_ListStackPop(Type, first_ptr, field_for_next) \ + DN_ListStackDetach(Type, first_ptr, field_for_next, first_ptr) // NOTE: Sentinel Doubly Linked List // Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. @@ -5934,15 +6258,15 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayAdd(ptr, ptr_size, max, item, add) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, add) #define DN_PArrayAddArena(ptr, ptr_size, ptr_max, arena, item, add) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, add) #define DN_PArrayAddPool(ptr, ptr_size, ptr_max, pool, item, add) DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, add) - #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Append) - #define DN_PArrayAppend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendArena(ptr, ptr_size, ptr_max, arena, item) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendPool(ptr, ptr_size, ptr_max, pool, item) DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendAssert(ptr, ptr_size, max, item) DN_TArrayAddArrayAssert(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append, DN_CallSiteNow) - #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Prepend) - #define DN_PArrayPrepend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Prepend) - #define DN_PArrayPrependArena(ptr, ptr_size, ptr_max, arena, item) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_ArrayAdd_Prepend) - #define DN_PArrayPrependPool(ptr, ptr_size, ptr_max, pool, item) DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_AddType_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_AddType_Append) + #define DN_PArrayAppendArena(ptr, ptr_size, ptr_max, arena, item) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_AddType_Append) + #define DN_PArrayAppendPool(ptr, ptr_size, ptr_max, pool, item) DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_AddType_Append) + #define DN_PArrayAppendAssert(ptr, ptr_size, max, item) DN_TArrayAddArrayAssert(ptr, ptr_size, max, &item, 1, DN_AddType_Append, DN_CallSiteNow) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_AddType_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_AddType_Prepend) + #define DN_PArrayPrependArena(ptr, ptr_size, ptr_max, arena, item) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_AddType_Prepend) + #define DN_PArrayPrependPool(ptr, ptr_size, ptr_max, pool, item) DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_AddType_Prepend) #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_TArrayEraseRange(ptr, ptr_size, begin_index, count, erase) #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_TArrayEraseRange(ptr, ptr_size, index, 1, erase) @@ -5996,15 +6320,15 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayAdd(ptr, ptr_size, max, item, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add) #define DN_PArrayAddArena(ptr, ptr_size, max, arena, item, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, add) #define DN_PArrayAddPool(ptr, ptr_size, max, pool, item, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, add) - #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Append) - #define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendArena(ptr, ptr_size, max, arena, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendPool(ptr, ptr_size, max, pool, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_ArrayAdd_Append) - #define DN_PArrayAppendAssert(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append, DN_CallSiteNow) - #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Prepend) - #define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Prepend) - #define DN_PArrayPrependArena(ptr, ptr_size, max, arena, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_ArrayAdd_Prepend) - #define DN_PArrayPrependPool(ptr, ptr_size, max, pool, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_AddType_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Append) + #define DN_PArrayAppendArena(ptr, ptr_size, max, arena, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_AddType_Append) + #define DN_PArrayAppendPool(ptr, ptr_size, max, pool, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_AddType_Append) + #define DN_PArrayAppendAssert(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Append, DN_CallSiteNow) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_AddType_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Prepend) + #define DN_PArrayPrependArena(ptr, ptr_size, max, arena, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_AddType_Prepend) + #define DN_PArrayPrependPool(ptr, ptr_size, max, pool, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_AddType_Prepend) #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase) #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase) @@ -6129,10 +6453,10 @@ DN_API void* DN_ArrayMakeArray (void * DN_API void* DN_ArrayMakeArrayAssert (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site); DN_API void* DN_ArrayMakeArrayArena (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize make_count, DN_ZMem z_mem); DN_API void* DN_ArrayMakeArrayPool (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize make_count, DN_ZMem z_mem); -DN_API void* DN_ArrayAddArray (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add); -DN_API void* DN_ArrayAddArrayAssert (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site); -DN_API void* DN_ArrayAddArrayArena (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Arena *arena, DN_ArrayAdd add); -DN_API void* DN_ArrayAddArrayPool (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Pool *pool, DN_ArrayAdd add); +DN_API void* DN_ArrayAddArray (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add); +DN_API void* DN_ArrayAddArrayAssert (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site); +DN_API void* DN_ArrayAddArrayArena (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Arena *arena, DN_AddType add); +DN_API void* DN_ArrayAddArrayPool (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Pool *pool, DN_AddType add); DN_API bool DN_ArrayResizePool (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); DN_API bool DN_ArrayResizeArena (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max); DN_API bool DN_ArrayReservePool (void **data, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); @@ -6155,24 +6479,24 @@ template T* DN_TArrayMakeArray (T *dat template T* DN_TArrayMakeArrayAssert (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site); template T* DN_TArrayMakeArrayAssertZ (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site); template T* DN_TArrayMakeArrayAssertNoZ (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site); -template T* DN_TArrayAddArray (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add); -template T* DN_TArrayAddArrayAssert (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site); +template T* DN_TArrayAddArray (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add); +template T* DN_TArrayAddArrayAssert (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site); template T* DN_TArrayMakeArrayArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count, DN_ZMem z_mem); template T* DN_TArrayMakeArrayArenaZ (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count); template T* DN_TArrayMakeArrayArenaNoZ (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count); template T* DN_TArrayMakeArrayPool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count, DN_ZMem z_mem); template T* DN_TArrayMakeArrayPoolZ (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count); template T* DN_TArrayMakeArrayPoolNoZ (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count); -template T* DN_TArrayAddArrayArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_ArrayAdd add); -template T* DN_TArrayAddArrayPool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_ArrayAdd add); +template T* DN_TArrayAddArrayArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add); +template T* DN_TArrayAddArrayPool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add); template T* DN_TArrayMakeArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_ZMem z_mem); template T* DN_TArrayMakeArenaZ (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena); template T* DN_TArrayMakeArenaNoZ (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena); template T* DN_TArrayMakePool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_ZMem z_mem); template T* DN_TArrayMakePoolZ (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool); template T* DN_TArrayMakePoolNoZ (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool); -template T* DN_TArrayAddArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_ArrayAdd add); -template T* DN_TArrayAddPool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_ArrayAdd add); +template T* DN_TArrayAddArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add); +template T* DN_TArrayAddPool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add); template bool DN_TArrayResizePool (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize new_max); template bool DN_TArrayResizeArena (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize new_max); template bool DN_TArrayReservePool (T **data, DN_USize *max, DN_Pool *pool, DN_USize new_max); @@ -6185,7 +6509,6 @@ template DN_USize DN_TArrayCopyPtrPool (T **da template DN_USize DN_TArrayCopyPtrPoolAssert (T **data, T const *src, DN_USize count, DN_Pool *pool, DN_CallSite call_site); #endif -DN_API void* DN_SinglyLLDetach (void **link, void **next); DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); @@ -6449,21 +6772,23 @@ DN_API void DN_TestVerifyBytesF #define DN_TestVerifyBytesNotEq(test_ptr, bytes, expect) DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " != " #expect), (bytes), (expect), /*expect_eq=*/ false, "") #if DN_WITH_TESTS -DN_API DN_TestCore DN_TestSuite (DN_Arena *arena_); +DN_API DN_TestCore DN_TestSuite (DN_Arena *arena_); #endif -DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); -DN_API void DN_LeakTrackDealloc_ (DN_LeakTracker *leak, void *ptr); -DN_API void DN_LeakDump_ (DN_LeakTracker *leak); +DN_API DN_MemDebuggerSnapshot DN_MemDebuggerSnapshotMake (DN_MemDebugger *debugger, DN_Arena *arena); +DN_API bool DN_MemDebuggerSnapshotPtrDataQSortComparePtrLT (void const *lhs, void const *rhs, void*); +DN_API void DN_MemDebuggerAlloc_ (DN_MemDebugger *debugger, void *ptr, DN_USize size, bool alloc_can_leak, DN_CallSite call_site); +DN_API void DN_MemDebuggerDealloc_ (DN_MemDebugger *debugger, void *ptr); +DN_API void DN_MemDebuggerDump_ (DN_MemDebugger *debugger); -#if defined(DN_LEAK_TRACKING) -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) -#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) -#define DN_LeakDump(leak) DN_LeakDump_(leak) +#if defined(DN_MEM_DEBUGGER) +#define DN_MemDebuggerAlloc(debugger, ptr, size, alloc_can_leak, call_site) DN_MemDebuggerAlloc_(debugger, ptr, size, alloc_can_leak, call_site) +#define DN_MemDebuggerDealloc(debugger, ptr) DN_MemDebuggerDealloc_(debugger, ptr) +#define DN_MemDebuggerDump(debugger) DN_MemDebuggerDump_(debugger) #else -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) -#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) -#define DN_LeakDump(leak) do { } while (0) +#define DN_MemDebuggerAlloc(debugger, ptr, size, alloc_can_leak, call_site) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) +#define DN_MemDebuggerDealloc(debugger, ptr) do { (void)ptr; } while (0) +#define DN_MemDebuggerDump(debugger) do { } while (0) #endif #if DN_WITH_OS @@ -6524,54 +6849,57 @@ DN_API void DN_OS_LoggerF // always unless you are on Emscripten where it'll use basic allocator (virtual memory is not // supported through Emscripten). -DN_API void * DN_OS_HeapBasicAlloc (DN_USize size); -DN_API void DN_OS_HeapBasicDealloc (void *ptr); -DN_API DN_Heap DN_OS_HeapInitBasic (); -DN_API DN_Heap DN_OS_HeapInitVirtual (); -DN_API DN_Heap DN_OS_HeapInitDefault (); +DN_API void * DN_OS_HeapBasicAlloc (DN_USize size); +DN_API void DN_OS_HeapBasicDealloc (void *ptr); +DN_API DN_Heap DN_OS_HeapInitBasic (); +DN_API DN_Heap DN_OS_HeapInitVirtual (); +DN_API DN_Heap DN_OS_HeapInitDefault (); -DN_API DN_Arena DN_OS_ArenaFromHeapVirtual (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); -DN_API DN_Arena DN_OS_ArenaFromHeapBasic (DN_U64 size, DN_MemFlags flags); +DN_API DN_Arena DN_OS_ArenaFromHeapVirtualCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); +#define DN_OS_ArenaFromHeapVirtual(reserve, commit, flags, name_cstr) DN_OS_ArenaFromHeapVirtualCallSite(reserve, commit, flags, DN_CallSiteNowNamed(name_cstr)) +DN_API DN_Arena DN_OS_ArenaFromHeapBasicCallSite (DN_U64 size, DN_MemFlags flags); +#define DN_OS_ArenaFromHeapBasic(size, flags, name_cstr) DN_OS_ArenaFromHeapBasic(size, flags, DN_CallSiteNowNamed(name_cstr)) -DN_API void * DN_OS_MemReserve (DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); -DN_API bool DN_OS_MemCommit (void *ptr, DN_USize count, DN_U32 page_flags); -DN_API void DN_OS_MemDecommit (void *ptr, DN_USize count); -DN_API void DN_OS_MemRelease (void *ptr, DN_USize count); -DN_API int DN_OS_MemProtect (void *ptr, DN_USize count, DN_U32 page_flags); +DN_API void * DN_OS_MemReserve (DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); +DN_API bool DN_OS_MemCommit (void *ptr, DN_USize count, DN_U32 page_flags); +DN_API void DN_OS_MemDecommit (void *ptr, DN_USize count); +DN_API void DN_OS_MemRelease (void *ptr, DN_USize count); +DN_API int DN_OS_MemProtect (void *ptr, DN_USize count, DN_U32 page_flags); -DN_API void * DN_OS_MemAlloc (DN_USize count, DN_ZMem z_mem); -DN_API void DN_OS_MemDealloc (void *ptr); +DN_API void * DN_OS_MemAlloc (DN_USize count, DN_ZMem z_mem); +DN_API void DN_OS_MemDealloc (void *ptr); -DN_API DN_Date DN_OS_DateLocalTimeNow (); -DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now (char date_seperator = '-', char hms_seperator = ':'); -DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8 (DN_Date time, char date_seperator = '-', char hms_seperator = ':'); -DN_API DN_U64 DN_OS_DateUnixTimeNs (); -#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000) -#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000)) -#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000)) -DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate (DN_Date date); -DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS (DN_U64 unix_ts_s); +DN_API DN_Date DN_OS_DateLocalTimeNow (); +DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now (char date_seperator = '-', char hms_seperator = ':'); +DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8 (DN_Date time, char date_seperator = '-', char hms_seperator = ':'); +DN_API DN_U64 DN_OS_DateUnixTimeNs (); +#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000) +#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000)) +#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000)) +DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate (DN_Date date); +DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS (DN_U64 unix_ts_s); -DN_API void DN_OS_GenBytesSecure (void *buffer, DN_U32 size); -DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value); -DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path); -DN_API DN_Str8 DN_OS_ExePath (DN_Arena *arena); -DN_API DN_Str8 DN_OS_ExeDir (DN_Arena *arena); -DN_API void DN_OS_SleepMs (DN_UInt milliseconds); +DN_API void DN_OS_GenBytesSecure (void *buffer, DN_U32 size); +DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value); +DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path); +DN_API DN_Str8 DN_OS_ExePath (DN_Arena *arena); +DN_API DN_Str8 DN_OS_ExeDir (DN_Arena *arena); +DN_API void DN_OS_OpenUrl (DN_Str8 url); +DN_API void DN_OS_SleepMs (DN_UInt milliseconds); -DN_API DN_U64 DN_OS_PerfCounterNow (); -DN_API DN_U64 DN_OS_PerfCounterFrequency (); -DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end); -DN_API DN_OSTimer DN_OS_TimerBegin (); -DN_API void DN_OS_TimerEnd (DN_OSTimer *timer); -DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer); -DN_API DN_U64 DN_OS_EstimateTscPerSecond (uint64_t duration_ms_to_gauge_tsc_frequency); +DN_API DN_U64 DN_OS_PerfCounterNow (); +DN_API DN_U64 DN_OS_PerfCounterFrequency (); +DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end); +DN_API DN_OSTimer DN_OS_TimerBegin (); +DN_API void DN_OS_TimerEnd (DN_OSTimer *timer); +DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer); +DN_API DN_U64 DN_OS_EstimateTscPerSecond (uint64_t duration_ms_to_gauge_tsc_frequency); // NOTE: OS File // Overview @@ -6673,6 +7001,18 @@ DN_API bool DN_OS_ConditionVariableWaitUntil (D DN_API void DN_OS_ConditionVariableSignal (DN_OSConditionVariable *cv); DN_API void DN_OS_ConditionVariableBroadcast (DN_OSConditionVariable *cv); +// NOTE: Windowing +DN_API DN_OSWindowMinimise DN_OS_WindowIsMinimised (DN_OSWindow *window); +DN_API DN_OSWindowMaximise DN_OS_WindowIsMaximised (DN_OSWindow *window); +DN_API void DN_OS_WindowSetTitle (DN_OSWindow *window, DN_Str8 title); +DN_API void DN_OS_WindowShow (DN_OSWindow *window, DN_OSWindowShow min); +DN_API void DN_OS_WindowMaximise (DN_OSWindow *window, DN_OSWindowMaximise maximise); +DN_API void DN_OS_WindowMinimise (DN_OSWindow *window, DN_OSWindowMinimise minimise); +DN_API void DN_OS_WindowBringToFront (DN_OSWindow *window); +DN_API void DN_OS_WindowFocus (DN_OSWindow *window); +DN_API bool DN_OS_WindowIsFocused (DN_OSWindow *window); +DN_API void DN_OS_WindowCustomTitlebarSet (DN_OSWindow *window, bool custom_titlebar); + // NOTE: Thread // Overview // Threading primitives that uses CreateThread on Win32 and pthreads everywhere else. Threads @@ -6933,14 +7273,14 @@ T *DN_TArrayMakeArrayAssertNoZ(T *data, DN_USize *count, DN_USize max, DN_USize } template -T *DN_TArrayAddArray(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +T *DN_TArrayAddArray(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add) { T* result = DN_Cast(T *)DN_ArrayAddArray(data, count, max, sizeof(*elems), elems, elems_count, add); return result; } template -T *DN_TArrayAddArrayAssert(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) +T *DN_TArrayAddArrayAssert(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site) { T* result = DN_Cast(T *)DN_ArrayAddArrayAssert(data, count, max, sizeof(*elems), elems, elems_count, add, call_site); return result; @@ -6989,14 +7329,14 @@ T *DN_TArrayMakeArrayPoolNoZ(T **data, DN_USize *count, DN_USize *max, DN_Pool * } template -T *DN_TArrayAddArrayArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +T *DN_TArrayAddArrayArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add) { T *result = DN_Cast(T *)DN_ArrayAddArrayArena(DN_Cast(void **)data, count, max, sizeof(**data), arena, elems, elems_count, add); return result; } template -T *DN_TArrayAddArrayPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +T *DN_TArrayAddArrayPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add) { T *result = DN_Cast(T *)DN_ArrayAddArrayPool(DN_Cast(void **)data, count, max, sizeof(**data), pool, elems, elems_count, add); return result; @@ -7045,14 +7385,14 @@ T *DN_TArrayMakePoolNoZ(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool) } template -T *DN_TArrayAddArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +T *DN_TArrayAddArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add) { T *result = DN_TArrayAddArrayArena(data, count, max, arena, elems, elems_count, add); return result; } template -T *DN_TArrayAddPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +T *DN_TArrayAddPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add) { T *result = DN_TArrayAddArrayPool(data, count, max, pool, elems, elems_count, add); return result; @@ -7363,19 +7703,27 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren #pragma comment(lib, "pathcch") #pragma comment(lib, "Shell32") // ShellExecuteW #pragma comment(lib, "shlwapi") + #pragma comment(lib, "user32") + + // TODO: Technically we only need this library for custom window titlebars to get access to DWM + // functions. Otherwise this API surface is not used in the DN OS layer. + #pragma comment(lib, "dwmapi") #endif #if defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER) || defined(_INC_WINDOWS) #define WIN32_LEAN_AND_MEAN - #include // LONG - #include // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc - #include // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc - #include // PathRelativePathTO - #include // PathCchCanonicalizeEx - #include // WinHttp* - #include // PROCESS_MEMORY_COUNTERS_EX2 - #include // OPENFILENAMEW + #include // LONG + #include // DwmExtendFrameIntoClientArea... + #include // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc + #include // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc + #include // MONITOR_DPI_TYPE + #include // PathRelativePathTO + #include // PathCchCanonicalizeEx + #include // WinHttp* + #include // PROCESS_MEMORY_COUNTERS_EX2 + #include // OPENFILENAMEW #include + #include // GET_X_PARAM #else DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union @@ -7407,9 +7755,10 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren typedef void * HANDLE; typedef HANDLE HLOCAL; - typedef unsigned __int64 WPARAM; - typedef LONG_PTR LPARAM; - typedef LONG_PTR LRESULT; + typedef unsigned __int64 WPARAM; + typedef LONG_PTR LPARAM; + typedef LONG_PTR LRESULT; + typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; #define MAX_PATH 260 @@ -7418,6 +7767,11 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren DWORD dwHighDateTime; } FILETIME, *PFILETIME, *LPFILETIME; + #define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff)) + #define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff)) + #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) + #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) + // NOTE: shared/winerror.h ///////////////////////////////////////////////////////////////////// // NOTE: GetModuleFileNameW #define ERROR_INSUFFICIENT_BUFFER 122L // dderror @@ -7437,6 +7791,7 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren typedef wchar_t WCHAR; // wc, 16-bit UNICODE character typedef CHAR * NPSTR, *LPSTR, *PSTR; typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR; + typedef const WCHAR* LPCWSTR, *PCWSTR; typedef long HRESULT; // NOTE: VirtualAlloc: Allocation Type @@ -7722,6 +8077,11 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren LONG y; } POINT, *PPOINT, *NPPOINT, *LPPOINT; + struct HMONITOR__ { + int unused; + }; + typedef struct HMONITOR__ *HMONITOR; + // NOTE: handleapi.h /////////////////////////////////////////////////////////////////////////// #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) @@ -8020,23 +8380,167 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren RECT rcDevice; #endif } WINDOWPLACEMENT; + + typedef struct tagWINDOWPOS { + HWND hwnd; + HWND hwndInsertAfter; + int x; + int y; + int cx; + int cy; + UINT flags; + } WINDOWPOS, *LPWINDOWPOS, *PWINDOWPOS; + + typedef struct tagNCCALCSIZE_PARAMS { + RECT rgrc[3]; + PWINDOWPOS lppos; + } NCCALCSIZE_PARAMS, *LPNCCALCSIZE_PARAMS; + typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT; + typedef LRESULT (__stdcall* WNDPROC)(HWND, UINT, WPARAM, LPARAM); + + #define WM_NCHITTEST 0x0084 + + #define WS_OVERLAPPED 0x00000000L + #define WS_POPUP 0x80000000L + #define WS_CHILD 0x40000000L + #define WS_MINIMIZE 0x20000000L + #define WS_VISIBLE 0x10000000L + #define WS_DISABLED 0x08000000L + #define WS_CLIPSIBLINGS 0x04000000L + #define WS_CLIPCHILDREN 0x02000000L + #define WS_MAXIMIZE 0x01000000L + #define WS_CAPTION 0x00C00000L /* WS_BORDER | WS_DLGFRAME */ + #define WS_BORDER 0x00800000L + #define WS_DLGFRAME 0x00400000L + #define WS_VSCROLL 0x00200000L + #define WS_HSCROLL 0x00100000L + #define WS_SYSMENU 0x00080000L + #define WS_THICKFRAME 0x00040000L + #define WS_GROUP 0x00020000L + #define WS_TABSTOP 0x00010000L + #define WS_MINIMIZEBOX 0x00020000L + #define WS_MAXIMIZEBOX 0x00010000L + #define WS_TILED WS_OVERLAPPED + #define WS_ICONIC WS_MINIMIZE + #define WS_SIZEBOX WS_THICKFRAME + #define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW + + #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ + WS_CAPTION | \ + WS_SYSMENU | \ + WS_THICKFRAME | \ + WS_MINIMIZEBOX | \ + WS_MAXIMIZEBOX) + #define SW_HIDE 0 #define SW_NORMAL 1 + #define SW_SHOWNORMAL 1 #define SW_MAXIMIZE 3 #define SW_SHOWNOACTIVATE 4 #define SW_SHOW 5 + #define SW_MINIMIZE 6 + #define SW_RESTORE 9 #define SW_FORCEMINIMIZE 11 + #define SWP_NOSIZE 0x0001 + #define SWP_NOMOVE 0x0002 + #define SWP_NOZORDER 0x0004 + #define SWP_NOREDRAW 0x0008 + #define SWP_NOACTIVATE 0x0010 + #define SWP_FRAMECHANGED 0x0020 /* The frame changed: send WM_NCCALCSIZE */ + #define SWP_SHOWWINDOW 0x0040 + #define SWP_HIDEWINDOW 0x0080 + #define SWP_NOCOPYBITS 0x0100 + #define SWP_NOOWNERZORDER 0x0200 /* Don't do owner Z ordering */ + #define SWP_NOSENDCHANGING 0x0400 /* Don't send WM_WINDOWPOSCHANGING */ + #define SWP_DRAWFRAME SWP_FRAMECHANGED + #define SWP_NOREPOSITION SWP_NOOWNERZORDER + + #define SM_CXEDGE 45 + #define SM_CYEDGE 46 + #define SM_CXFRAME 32 + #define SM_CYFRAME 33 + #define SM_CXPADDEDBORDER 92 + + #define GWLP_WNDPROC (-4) + + #define GWL_STYLE (-16) + + #define WS_OVERLAPPED 0x00000000L + + #define WM_NCCALCSIZE 0x0083 + + // WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes + #define HTERROR (-2) + #define HTTRANSPARENT (-1) + #define HTNOWHERE 0 + #define HTCLIENT 1 + #define HTCAPTION 2 + #define HTSYSMENU 3 + #define HTGROWBOX 4 + #define HTSIZE HTGROWBOX + #define HTMENU 5 + #define HTHSCROLL 6 + #define HTVSCROLL 7 + #define HTMINBUTTON 8 + #define HTMAXBUTTON 9 + #define HTLEFT 10 + #define HTRIGHT 11 + #define HTTOP 12 + #define HTTOPLEFT 13 + #define HTTOPRIGHT 14 + #define HTBOTTOM 15 + #define HTBOTTOMLEFT 16 + #define HTBOTTOMRIGHT 17 + #define HTBORDER 18 + #define HTREDUCE HTMINBUTTON + #define HTZOOM HTMAXBUTTON + #define HTSIZEFIRST HTLEFT + #define HTSIZELAST HTBOTTOMRIGHT + #if(WINVER >= 0x0400) + #define HTOBJECT 19 + #define HTCLOSE 20 + #define HTHELP 21 + #endif /* WINVER >= 0x0400 */ + extern "C" { - __declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect); - __declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); - __declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax); - __declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow); - __declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl); + __declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect); + __declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); + __declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax); + __declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow); + __declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl); + __declspec(dllimport) BOOL __stdcall IsIconic (HWND hWnd); + __declspec(dllimport) BOOL __stdcall IsZoomed (HWND hWnd); + __declspec(dllimport) BOOL __stdcall BringWindowToTop (HWND hWnd); + __declspec(dllimport) BOOL __stdcall SetForegroundWindow (HWND hWnd); + __declspec(dllimport) HWND __stdcall SetFocus (HWND hWnd); + __declspec(dllimport) HWND __stdcall GetForegroundWindow (); + __declspec(dllimport) BOOL __stdcall SetWindowTextW (HWND hWnd, LPCWSTR lpString); + __declspec(dllimport) UINT __stdcall GetDoubleClickTime (); + __declspec(dllimport) LONG_PTR __stdcall GetWindowLongPtrW (HWND hWnd, int nIndex); + __declspec(dllimport) LONG_PTR __stdcall SetWindowLongPtrW (HWND hWnd, int nIndex, LONG_PTR dwNewLong); + __declspec(dllimport) LONG __stdcall GetWindowLongW (HWND hWnd, int nIndex); + __declspec(dllimport) LRESULT __stdcall CallWindowProcW (WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + __declspec(dllimport) int __stdcall GetSystemMetrics (int nIndex); + __declspec(dllimport) BOOL __stdcall ScreenToClient (HWND hWnd, LPPOINT lpPoint); + __declspec(dllimport) LRESULT __stdcall DefWindowProcW (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + } + // NOTE: um/dwmapi.h ////////////////////////////////////////////////////////////////////////// + typedef struct _MARGINS + { + int cxLeftWidth; // width of left border that retains its size + int cxRightWidth; // width of right border that retains its size + int cyTopHeight; // height of top border that retains its size + int cyBottomHeight; // height of bottom border that retains its size + } MARGINS, *PMARGINS; + + extern "C" + { + __declspec(dllimport) HRESULT __stdcall DwmExtendFrameIntoClientArea(HWND hWnd, const MARGINS* pMarInset); } // NOTE: um/wininet.h ////////////////////////////////////////////////////////////////////////// @@ -8538,7 +9042,6 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren // NOTE: um/shellapi.h ///////////////////////////////////////////////////////////////////////// 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); } @@ -8707,6 +9210,14 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren #define EXCEPTION_CONTINUE_SEARCH 0 #define EXCEPTION_CONTINUE_EXECUTION (-1) + // NOTE: um/shellscalingapi.h + typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI + } MONITOR_DPI_TYPE; + DN_MSVC_WARNING_POP #endif // !defined(_INC_WINDOWS) #endif // !defined(DN_OS_WINDOWS_H) @@ -8733,6 +9244,18 @@ struct DN_OSW32FolderIteratorW wchar_t file_name_buf[512]; }; +struct DN_OSW32Window +{ + HWND hwnd; + // NOTE: For windows with custom title bars, this should be the rect in framebuffer units that + // when the mouse is down will move the window, or if the mouse is double-clicked then toggle the + // maximisation of the window. This rect should not include any custom drawn menu buttons so that + // those buttons can be handled by your application. + DN_Rect custom_title_bar_rect_that_is_draggable; + bool custom_title_bar; + DN_OSW32Window *next; +}; + enum DN_OSW32SyncPrimitiveType { DN_OSW32SyncPrimitiveType_Semaphore, @@ -8754,42 +9277,59 @@ struct DN_OSW32SyncPrimitive DN_OSW32SyncPrimitive *next; }; -typedef HRESULT DN_OSW32SetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription); +typedef HRESULT DN_OSW32SetThreadDescriptionFunc (HANDLE hThread, PWSTR const lpThreadDescription); +typedef BOOL DN_OSW32SetProcessDpiAwarenessContextFunc(void* value); +typedef UINT DN_OSW32GetDpiForWindowFunc (HWND hwnd); +typedef HRESULT DN_OSW32GetDpiForMonitorFunc (HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY); +typedef int DN_OSW32GetSystemMetricsForDpiFunc (int nIndex, UINT dpi); + +#define DN_OSW32DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((void*)-4) struct DN_OSW32Core { - DN_OSW32SetThreadDescriptionFunc *set_thread_description; - LARGE_INTEGER qpc_frequency; - void *bcrypt_rng_handle; - bool bcrypt_init_success; - bool sym_initialised; + // NOTE: These functions are dynamically loaded because they might not be present on the user's + // machine. For example the GetDpiForWindow is Windows 10+ only. + DN_OSW32SetThreadDescriptionFunc *set_thread_description; + DN_OSW32GetDpiForWindowFunc *get_dpi_for_window; + DN_OSW32GetDpiForMonitorFunc *get_dpi_for_monitor; + DN_OSW32GetSystemMetricsForDpiFunc *get_system_metrics_for_dpi; - CRITICAL_SECTION sync_primitive_free_list_mutex; - DN_OSW32SyncPrimitive *sync_primitive_free_list; + LARGE_INTEGER qpc_frequency; + void* bcrypt_rng_handle; + bool bcrypt_init_success; + bool sym_initialised; + CRITICAL_SECTION sync_primitive_free_list_mutex; + DN_OSW32SyncPrimitive* sync_primitive_free_list; + + DN_OSW32Window* windows; + WNDPROC fallback_wnd_proc; }; -DN_API DN_OSW32Core* DN_OS_W32GetCore (); -DN_API void DN_OS_W32ThreadSetName (DN_Str8 name); +DN_API void DN_OS_W32Init (DN_OSW32Core *w32); +DN_API DN_OSW32Core* DN_OS_W32GetCore (); +DN_API void DN_OS_W32ThreadSetName (DN_Str8 name); +DN_API DN_OSW32Window* DN_OS_W32WindowFromHwndMaybeAlloc(DN_OSW32Core *w32, HWND hwnd, DN_Arena *arena); +DN_API LRESULT __stdcall DN_OS_W32WindowProcCustomTitlebar(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param); -DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg (DN_Arena *arena, DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc (DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32LastError (DN_Arena *arena); -DN_API DN_OSW32Error DN_OS_W32LastErrorAlloc (); -DN_API void DN_OS_W32MakeProcessDPIAware (); +DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg (DN_Arena *arena, DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc (DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32LastError (DN_Arena *arena); +DN_API DN_OSW32Error DN_OS_W32LastErrorAlloc (); +DN_API void DN_OS_W32MakeProcessDPIAware (); // NOTE: Windows Str8 <-> Str16 -DN_API DN_Str16 DN_OS_W32Str8ToStr16 (DN_Arena *arena, DN_Str8 src); -DN_API int DN_OS_W32Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size); -DN_API DN_Str8 DN_OS_W32Str16ToStr8 (DN_Arena *arena, DN_Str16 src); -DN_API int DN_OS_W32Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size); -DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap (DN_Str16 src); +DN_API DN_Str16 DN_OS_W32Str8ToStr16 (DN_Arena *arena, DN_Str8 src); +DN_API int DN_OS_W32Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size); +DN_API DN_Str8 DN_OS_W32Str16ToStr8 (DN_Arena *arena, DN_Str16 src); +DN_API int DN_OS_W32Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size); +DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap (DN_Str16 src); // NOTE: Path navigation -DN_API DN_Str16 DN_OS_W32EXEPathW (DN_Arena *arena); -DN_API DN_Str16 DN_OS_W32EXEDirW (DN_Arena *arena); -DN_API DN_Str8 DN_OS_W32WorkingDir (DN_Arena *arena, DN_Str8 suffix); -DN_API DN_Str16 DN_OS_W32WorkingDirW (DN_Arena *arena, DN_Str16 suffix); -DN_API bool DN_OS_W32DirWIterate (DN_Str16 path, DN_OSW32FolderIteratorW *it); +DN_API DN_Str16 DN_OS_W32EXEPathW (DN_Arena *arena); +DN_API DN_Str16 DN_OS_W32EXEDirW (DN_Arena *arena); +DN_API DN_Str8 DN_OS_W32WorkingDir (DN_Arena *arena, DN_Str8 suffix); +DN_API DN_Str16 DN_OS_W32WorkingDirW (DN_Arena *arena, DN_Str16 suffix); +DN_API bool DN_OS_W32DirWIterate (DN_Str16 path, DN_OSW32FolderIteratorW *it); #endif // !defined(DN_OS_W32_H) #elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN) // DN: Single header generator commented out => #include "OS/dn_os_posix.h" diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index 2744b08..f68c383 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -1057,6 +1057,36 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs return result; } +DN_API void DN_OS_W32Init(DN_OSW32Core *w32) +{ + InitializeCriticalSection(&w32->sync_primitive_free_list_mutex); + QueryPerformanceFrequency(&w32->qpc_frequency); + + // NOTE: DPI + HMODULE user32 = LoadLibraryA("user32.dll"); + if (user32) { + w32->get_dpi_for_window = (DN_OSW32GetDpiForWindowFunc* )GetProcAddress(user32, "GetDpiForWindow"); + w32->get_dpi_for_monitor = (DN_OSW32GetDpiForMonitorFunc * )GetProcAddress(user32, "GetDpiForMonitor"); + w32->get_system_metrics_for_dpi = (DN_OSW32GetSystemMetricsForDpiFunc *)GetProcAddress(user32, "GetSystemMetricsForDpi"); + FreeLibrary(user32); + } + + // NOTE: SetThreadDesc + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) { + w32->set_thread_description = DN_Cast(DN_OSW32SetThreadDescriptionFunc *) GetProcAddress(kernel32, "SetThreadDescription"); + FreeLibrary(kernel32); + } + + // NOTE: win32 bcrypt + wchar_t const BCRYPT_ALGORITHM[] = L"RNG"; + long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/); + if (w32->bcrypt_rng_handle && init_status == 0) + w32->bcrypt_init_success = true; + else + DN_LogErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status); +} + DN_API DN_OSW32Core *DN_OS_W32GetCore() { DN_Core *dn = DN_Get(); @@ -1456,6 +1486,160 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) DN_TcScratchEnd(&scratch); } +DN_API DN_OSW32Window* DN_OS_W32WindowFromHwndMaybeAlloc(DN_OSW32Core *w32, HWND hwnd, DN_Arena *arena) +{ + DN_OSW32Window *result = nullptr; + for (DN_OSW32Window *check = w32->windows; !result && check; check = check->next) { + if (check->hwnd == hwnd) + result = check; + } + + if (!result && arena) { + result = DN_ArenaNewZ(arena, DN_OSW32Window); + if (result) { + result->hwnd = hwnd; + if (w32->windows) + w32->windows->next = result; + result->next = w32->windows; + w32->windows = result; + } + } + return result; +} + +DN_API LRESULT __stdcall DN_OS_W32WindowProcCustomTitlebar(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) +{ + DN_OSW32Core *w32 = DN_OS_W32GetCore(); + DN_AssertF(w32, "Using this window proc requires that DN's OS layer has been initialised"); + + LRESULT result = 0; + bool handled = true; + switch (msg) { + // NOTE: Sent when the size and position of a window's client area must be calculated. By + // processing this message, an application can control the content of the window's client area + // when the size or position of the window changes. + case WM_NCCALCSIZE: { + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, nullptr); + if (w32_window && w32_window->custom_title_bar) { + // NOTE: Code adapted from raddebugger + // https://github.com/EpicGames/raddebugger/blob/7a5a649b89f6a06abdbaee61540468fca9db58f9/src/win32/window_manager/win32_window_manager.c#L668 + // + // The goal here is to subtract away the title bar from the window and restore the pixel + // border that native Windows renders windows with. Note that on Windows 11 a non-maximised + // window has rounded corners which eats into the available client space that is normally + // otherwise eaten into from the native titlebar. + DWORD window_style = GetWindowLongW(hwnd, GWL_STYLE); + bool is_fullscreen = !(window_style & WS_OVERLAPPEDWINDOW); + if (is_fullscreen) { + handled = false; + } else { + DN_UInt dpi = w32->get_dpi_for_window ? w32->get_dpi_for_window(hwnd) : 96; + int frame_x = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXFRAME, dpi) : GetSystemMetrics(SM_CXFRAME); + int frame_y = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYFRAME, dpi) : GetSystemMetrics(SM_CYFRAME); + int padding = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXPADDEDBORDER, dpi) : GetSystemMetrics(SM_CXPADDEDBORDER); + + RECT *rect = w_param == 0 ? DN_Cast(RECT *)l_param : (DN_Cast(NCCALCSIZE_PARAMS *)l_param)->rgrc; + rect->right -= frame_x + padding; + rect->left += frame_x + padding; + rect->bottom -= frame_y + padding; + bool is_maximised = IsZoomed(hwnd) != 0; + if (is_maximised) { + rect->top += frame_y + padding; + // If we do not do this hidden taskbar can not be unhidden on mouse hover + // Unfortunately it can create an ugly bottom border when maximized... + rect->bottom -= 1; + } else { + int edge_height = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYEDGE, dpi) : GetSystemMetrics(SM_CYEDGE); + rect->top += edge_height; + } + } + } else { + handled = false; + } + } break; + + // NOTE: Sent to a window in order to determine what part of the window corresponds to a + // particular screen coordinate. This can happen, for example, when the cursor moves, when a + // mouse button is pressed or released, or in response to a call to a function such as + // WindowFromPoint. If the mouse is not captured, the message is sent to the window beneath + // the cursor. Otherwise, the message is sent to the window that has captured the mouse. + case WM_NCHITTEST: { + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, nullptr); + if (w32_window && w32_window->custom_title_bar) { + // NOTE: Code adapted from raddebugger + // https://github.com/EpicGames/raddebugger/blob/7a5a649b89f6a06abdbaee61540468fca9db58f9/src/win32/window_manager/win32_window_manager.c#L668 + // + // Since we removed the titlebar from the window, the resize handlers/grabbers at the top of + // the window are no longer functional. We have to reimplement it ourselves. + DWORD window_style = GetWindowLongW(hwnd, GWL_STYLE); + bool is_fullscreen = !(window_style & WS_OVERLAPPEDWINDOW); + handled = false; + if (!is_fullscreen) { + // Let the default procedure handle resizing areas + if (w32->fallback_wnd_proc) + result = CallWindowProcW(w32->fallback_wnd_proc, hwnd, msg, w_param, l_param); + else + result = DefWindowProcW(hwnd, msg, w_param, l_param); + + switch (result) { + case HTNOWHERE: + case HTRIGHT: + case HTLEFT: + case HTTOPLEFT: + case HTTOPRIGHT: + case HTBOTTOMRIGHT: + case HTBOTTOM: + case HTBOTTOMLEFT: { + handled = true; + } break; + } + + if (!handled) { + POINT pos_monitor = {}; + pos_monitor.x = GET_X_LPARAM(l_param); + pos_monitor.y = GET_Y_LPARAM(l_param); + handled = true; + + POINT pos_client = pos_monitor; + ScreenToClient(hwnd, &pos_client); + + DN_V2F32 title_bar_min = w32_window->custom_title_bar_rect_that_is_draggable.pos; + DN_V2F32 title_bar_max = title_bar_min + w32_window->custom_title_bar_rect_that_is_draggable.size; + DN_UInt dpi = w32->get_dpi_for_window ? w32->get_dpi_for_window(hwnd) : 96; + int frame_y = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CYFRAME, dpi) : GetSystemMetrics(SM_CYFRAME); + int padding = w32->get_system_metrics_for_dpi ? w32->get_system_metrics_for_dpi(SM_CXPADDEDBORDER, dpi) : GetSystemMetrics(SM_CXPADDEDBORDER); + bool is_over_top_resize = pos_client.y >= 0 && pos_client.y < (frame_y + padding); + bool is_over_title_bar = pos_client.x >= title_bar_min.x && pos_client.x < title_bar_max.x && + pos_client.y >= title_bar_min.y && pos_client.y < title_bar_max.y; + bool is_maximised = IsZoomed(hwnd) != 0; + if (!is_maximised && is_over_top_resize) + result = HTTOP; + else if (is_over_title_bar) + result = HTCAPTION; + else + result = HTCLIENT; + } + } + } else { + handled = false; + } + } break; + + default: { + handled = false; + } break; + } + + if (!handled) { + if (w32->fallback_wnd_proc) { + result = CallWindowProcW(w32->fallback_wnd_proc, hwnd, msg, w_param, l_param); + } else { + result = DefWindowProcW(hwnd, msg, w_param, l_param); + } + } + return result; +} + DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code) { DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; diff --git a/Source/OS/dn_os_w32.h b/Source/OS/dn_os_w32.h index d226a9e..b012c28 100644 --- a/Source/OS/dn_os_w32.h +++ b/Source/OS/dn_os_w32.h @@ -20,6 +20,18 @@ struct DN_OSW32FolderIteratorW wchar_t file_name_buf[512]; }; +struct DN_OSW32Window +{ + HWND hwnd; + // NOTE: For windows with custom title bars, this should be the rect in framebuffer units that + // when the mouse is down will move the window, or if the mouse is double-clicked then toggle the + // maximisation of the window. This rect should not include any custom drawn menu buttons so that + // those buttons can be handled by your application. + DN_Rect custom_title_bar_rect_that_is_draggable; + bool custom_title_bar; + DN_OSW32Window *next; +}; + enum DN_OSW32SyncPrimitiveType { DN_OSW32SyncPrimitiveType_Semaphore, @@ -41,40 +53,57 @@ struct DN_OSW32SyncPrimitive DN_OSW32SyncPrimitive *next; }; -typedef HRESULT DN_OSW32SetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription); +typedef HRESULT DN_OSW32SetThreadDescriptionFunc (HANDLE hThread, PWSTR const lpThreadDescription); +typedef BOOL DN_OSW32SetProcessDpiAwarenessContextFunc(void* value); +typedef UINT DN_OSW32GetDpiForWindowFunc (HWND hwnd); +typedef HRESULT DN_OSW32GetDpiForMonitorFunc (HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY); +typedef int DN_OSW32GetSystemMetricsForDpiFunc (int nIndex, UINT dpi); + +#define DN_OSW32DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((void*)-4) struct DN_OSW32Core { - DN_OSW32SetThreadDescriptionFunc *set_thread_description; - LARGE_INTEGER qpc_frequency; - void *bcrypt_rng_handle; - bool bcrypt_init_success; - bool sym_initialised; + // NOTE: These functions are dynamically loaded because they might not be present on the user's + // machine. For example the GetDpiForWindow is Windows 10+ only. + DN_OSW32SetThreadDescriptionFunc *set_thread_description; + DN_OSW32GetDpiForWindowFunc *get_dpi_for_window; + DN_OSW32GetDpiForMonitorFunc *get_dpi_for_monitor; + DN_OSW32GetSystemMetricsForDpiFunc *get_system_metrics_for_dpi; - CRITICAL_SECTION sync_primitive_free_list_mutex; - DN_OSW32SyncPrimitive *sync_primitive_free_list; + LARGE_INTEGER qpc_frequency; + void* bcrypt_rng_handle; + bool bcrypt_init_success; + bool sym_initialised; + CRITICAL_SECTION sync_primitive_free_list_mutex; + DN_OSW32SyncPrimitive* sync_primitive_free_list; + + DN_OSW32Window* windows; + WNDPROC fallback_wnd_proc; }; -DN_API DN_OSW32Core* DN_OS_W32GetCore (); -DN_API void DN_OS_W32ThreadSetName (DN_Str8 name); +DN_API void DN_OS_W32Init (DN_OSW32Core *w32); +DN_API DN_OSW32Core* DN_OS_W32GetCore (); +DN_API void DN_OS_W32ThreadSetName (DN_Str8 name); +DN_API DN_OSW32Window* DN_OS_W32WindowFromHwndMaybeAlloc(DN_OSW32Core *w32, HWND hwnd, DN_Arena *arena); +DN_API LRESULT __stdcall DN_OS_W32WindowProcCustomTitlebar(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param); -DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg (DN_Arena *arena, DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc (DN_U32 error_code); -DN_API DN_OSW32Error DN_OS_W32LastError (DN_Arena *arena); -DN_API DN_OSW32Error DN_OS_W32LastErrorAlloc (); -DN_API void DN_OS_W32MakeProcessDPIAware (); +DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsg (DN_Arena *arena, DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32ErrorCodeToMsgAlloc (DN_U32 error_code); +DN_API DN_OSW32Error DN_OS_W32LastError (DN_Arena *arena); +DN_API DN_OSW32Error DN_OS_W32LastErrorAlloc (); +DN_API void DN_OS_W32MakeProcessDPIAware (); // NOTE: Windows Str8 <-> Str16 -DN_API DN_Str16 DN_OS_W32Str8ToStr16 (DN_Arena *arena, DN_Str8 src); -DN_API int DN_OS_W32Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size); -DN_API DN_Str8 DN_OS_W32Str16ToStr8 (DN_Arena *arena, DN_Str16 src); -DN_API int DN_OS_W32Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size); -DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap (DN_Str16 src); +DN_API DN_Str16 DN_OS_W32Str8ToStr16 (DN_Arena *arena, DN_Str8 src); +DN_API int DN_OS_W32Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size); +DN_API DN_Str8 DN_OS_W32Str16ToStr8 (DN_Arena *arena, DN_Str16 src); +DN_API int DN_OS_W32Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size); +DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap (DN_Str16 src); // NOTE: Path navigation -DN_API DN_Str16 DN_OS_W32EXEPathW (DN_Arena *arena); -DN_API DN_Str16 DN_OS_W32EXEDirW (DN_Arena *arena); -DN_API DN_Str8 DN_OS_W32WorkingDir (DN_Arena *arena, DN_Str8 suffix); -DN_API DN_Str16 DN_OS_W32WorkingDirW (DN_Arena *arena, DN_Str16 suffix); -DN_API bool DN_OS_W32DirWIterate (DN_Str16 path, DN_OSW32FolderIteratorW *it); +DN_API DN_Str16 DN_OS_W32EXEPathW (DN_Arena *arena); +DN_API DN_Str16 DN_OS_W32EXEDirW (DN_Arena *arena); +DN_API DN_Str8 DN_OS_W32WorkingDir (DN_Arena *arena, DN_Str8 suffix); +DN_API DN_Str16 DN_OS_W32WorkingDirW (DN_Arena *arena, DN_Str16 suffix); +DN_API bool DN_OS_W32DirWIterate (DN_Str16 path, DN_OSW32FolderIteratorW *it); #endif // !defined(DN_OS_W32_H) diff --git a/Source/OS/dn_os_windows.h b/Source/OS/dn_os_windows.h index 15a258e..5dee44a 100644 --- a/Source/OS/dn_os_windows.h +++ b/Source/OS/dn_os_windows.h @@ -13,19 +13,27 @@ #pragma comment(lib, "pathcch") #pragma comment(lib, "Shell32") // ShellExecuteW #pragma comment(lib, "shlwapi") + #pragma comment(lib, "user32") + + // TODO: Technically we only need this library for custom window titlebars to get access to DWM + // functions. Otherwise this API surface is not used in the DN OS layer. + #pragma comment(lib, "dwmapi") #endif #if defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER) || defined(_INC_WINDOWS) #define WIN32_LEAN_AND_MEAN - #include // LONG - #include // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc - #include // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc - #include // PathRelativePathTO - #include // PathCchCanonicalizeEx - #include // WinHttp* - #include // PROCESS_MEMORY_COUNTERS_EX2 - #include // OPENFILENAMEW + #include // LONG + #include // DwmExtendFrameIntoClientArea... + #include // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc + #include // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc + #include // MONITOR_DPI_TYPE + #include // PathRelativePathTO + #include // PathCchCanonicalizeEx + #include // WinHttp* + #include // PROCESS_MEMORY_COUNTERS_EX2 + #include // OPENFILENAMEW #include + #include // GET_X_PARAM #else DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union @@ -57,9 +65,10 @@ typedef void * HANDLE; typedef HANDLE HLOCAL; - typedef unsigned __int64 WPARAM; - typedef LONG_PTR LPARAM; - typedef LONG_PTR LRESULT; + typedef unsigned __int64 WPARAM; + typedef LONG_PTR LPARAM; + typedef LONG_PTR LRESULT; + typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; #define MAX_PATH 260 @@ -68,6 +77,11 @@ DWORD dwHighDateTime; } FILETIME, *PFILETIME, *LPFILETIME; + #define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff)) + #define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff)) + #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) + #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) + // NOTE: shared/winerror.h ///////////////////////////////////////////////////////////////////// // NOTE: GetModuleFileNameW #define ERROR_INSUFFICIENT_BUFFER 122L // dderror @@ -87,6 +101,7 @@ typedef wchar_t WCHAR; // wc, 16-bit UNICODE character typedef CHAR * NPSTR, *LPSTR, *PSTR; typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR; + typedef const WCHAR* LPCWSTR, *PCWSTR; typedef long HRESULT; // NOTE: VirtualAlloc: Allocation Type @@ -372,6 +387,11 @@ LONG y; } POINT, *PPOINT, *NPPOINT, *LPPOINT; + struct HMONITOR__ { + int unused; + }; + typedef struct HMONITOR__ *HMONITOR; + // NOTE: handleapi.h /////////////////////////////////////////////////////////////////////////// #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) @@ -670,7 +690,59 @@ RECT rcDevice; #endif } WINDOWPLACEMENT; + + typedef struct tagWINDOWPOS { + HWND hwnd; + HWND hwndInsertAfter; + int x; + int y; + int cx; + int cy; + UINT flags; + } WINDOWPOS, *LPWINDOWPOS, *PWINDOWPOS; + + typedef struct tagNCCALCSIZE_PARAMS { + RECT rgrc[3]; + PWINDOWPOS lppos; + } NCCALCSIZE_PARAMS, *LPNCCALCSIZE_PARAMS; + typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT; + typedef LRESULT (__stdcall* WNDPROC)(HWND, UINT, WPARAM, LPARAM); + + #define WM_NCHITTEST 0x0084 + + #define WS_OVERLAPPED 0x00000000L + #define WS_POPUP 0x80000000L + #define WS_CHILD 0x40000000L + #define WS_MINIMIZE 0x20000000L + #define WS_VISIBLE 0x10000000L + #define WS_DISABLED 0x08000000L + #define WS_CLIPSIBLINGS 0x04000000L + #define WS_CLIPCHILDREN 0x02000000L + #define WS_MAXIMIZE 0x01000000L + #define WS_CAPTION 0x00C00000L /* WS_BORDER | WS_DLGFRAME */ + #define WS_BORDER 0x00800000L + #define WS_DLGFRAME 0x00400000L + #define WS_VSCROLL 0x00200000L + #define WS_HSCROLL 0x00100000L + #define WS_SYSMENU 0x00080000L + #define WS_THICKFRAME 0x00040000L + #define WS_GROUP 0x00020000L + #define WS_TABSTOP 0x00010000L + #define WS_MINIMIZEBOX 0x00020000L + #define WS_MAXIMIZEBOX 0x00010000L + #define WS_TILED WS_OVERLAPPED + #define WS_ICONIC WS_MINIMIZE + #define WS_SIZEBOX WS_THICKFRAME + #define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW + + #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ + WS_CAPTION | \ + WS_SYSMENU | \ + WS_THICKFRAME | \ + WS_MINIMIZEBOX | \ + WS_MAXIMIZEBOX) + #define SW_HIDE 0 #define SW_NORMAL 1 @@ -678,22 +750,107 @@ #define SW_MAXIMIZE 3 #define SW_SHOWNOACTIVATE 4 #define SW_SHOW 5 + #define SW_MINIMIZE 6 #define SW_RESTORE 9 #define SW_FORCEMINIMIZE 11 + #define SWP_NOSIZE 0x0001 + #define SWP_NOMOVE 0x0002 + #define SWP_NOZORDER 0x0004 + #define SWP_NOREDRAW 0x0008 + #define SWP_NOACTIVATE 0x0010 + #define SWP_FRAMECHANGED 0x0020 /* The frame changed: send WM_NCCALCSIZE */ + #define SWP_SHOWWINDOW 0x0040 + #define SWP_HIDEWINDOW 0x0080 + #define SWP_NOCOPYBITS 0x0100 + #define SWP_NOOWNERZORDER 0x0200 /* Don't do owner Z ordering */ + #define SWP_NOSENDCHANGING 0x0400 /* Don't send WM_WINDOWPOSCHANGING */ + #define SWP_DRAWFRAME SWP_FRAMECHANGED + #define SWP_NOREPOSITION SWP_NOOWNERZORDER + + #define SM_CXEDGE 45 + #define SM_CYEDGE 46 + #define SM_CXFRAME 32 + #define SM_CYFRAME 33 + #define SM_CXPADDEDBORDER 92 + + #define GWLP_WNDPROC (-4) + + #define GWL_STYLE (-16) + + #define WS_OVERLAPPED 0x00000000L + + #define WM_NCCALCSIZE 0x0083 + + // WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes + #define HTERROR (-2) + #define HTTRANSPARENT (-1) + #define HTNOWHERE 0 + #define HTCLIENT 1 + #define HTCAPTION 2 + #define HTSYSMENU 3 + #define HTGROWBOX 4 + #define HTSIZE HTGROWBOX + #define HTMENU 5 + #define HTHSCROLL 6 + #define HTVSCROLL 7 + #define HTMINBUTTON 8 + #define HTMAXBUTTON 9 + #define HTLEFT 10 + #define HTRIGHT 11 + #define HTTOP 12 + #define HTTOPLEFT 13 + #define HTTOPRIGHT 14 + #define HTBOTTOM 15 + #define HTBOTTOMLEFT 16 + #define HTBOTTOMRIGHT 17 + #define HTBORDER 18 + #define HTREDUCE HTMINBUTTON + #define HTZOOM HTMAXBUTTON + #define HTSIZEFIRST HTLEFT + #define HTSIZELAST HTBOTTOMRIGHT + #if(WINVER >= 0x0400) + #define HTOBJECT 19 + #define HTCLOSE 20 + #define HTHELP 21 + #endif /* WINVER >= 0x0400 */ + extern "C" { - __declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect); - __declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); - __declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax); - __declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow); - __declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl); - __declspec(dllimport) BOOL __stdcall IsIconic (HWND hWnd); - __declspec(dllimport) BOOL __stdcall BringWindowToTop (HWND hWnd); - __declspec(dllimport) BOOL __stdcall SetForegroundWindow (HWND hWnd); - __declspec(dllimport) HWND __stdcall SetFocus (HWND hWnd); - __declspec(dllimport) HWND __stdcall GetForegroundWindow (); - __declspec(dllimport) BOOL __stdcall SetWindowTextW (HWND hWnd, LPCWSTR lpString); + __declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect); + __declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); + __declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax); + __declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow); + __declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl); + __declspec(dllimport) BOOL __stdcall IsIconic (HWND hWnd); + __declspec(dllimport) BOOL __stdcall IsZoomed (HWND hWnd); + __declspec(dllimport) BOOL __stdcall BringWindowToTop (HWND hWnd); + __declspec(dllimport) BOOL __stdcall SetForegroundWindow (HWND hWnd); + __declspec(dllimport) HWND __stdcall SetFocus (HWND hWnd); + __declspec(dllimport) HWND __stdcall GetForegroundWindow (); + __declspec(dllimport) BOOL __stdcall SetWindowTextW (HWND hWnd, LPCWSTR lpString); + __declspec(dllimport) UINT __stdcall GetDoubleClickTime (); + __declspec(dllimport) LONG_PTR __stdcall GetWindowLongPtrW (HWND hWnd, int nIndex); + __declspec(dllimport) LONG_PTR __stdcall SetWindowLongPtrW (HWND hWnd, int nIndex, LONG_PTR dwNewLong); + __declspec(dllimport) LONG __stdcall GetWindowLongW (HWND hWnd, int nIndex); + __declspec(dllimport) LRESULT __stdcall CallWindowProcW (WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + __declspec(dllimport) int __stdcall GetSystemMetrics (int nIndex); + __declspec(dllimport) BOOL __stdcall ScreenToClient (HWND hWnd, LPPOINT lpPoint); + __declspec(dllimport) LRESULT __stdcall DefWindowProcW (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + } + + // NOTE: um/dwmapi.h ////////////////////////////////////////////////////////////////////////// + typedef struct _MARGINS + { + int cxLeftWidth; // width of left border that retains its size + int cxRightWidth; // width of right border that retains its size + int cyTopHeight; // height of top border that retains its size + int cyBottomHeight; // height of bottom border that retains its size + } MARGINS, *PMARGINS; + + extern "C" + { + __declspec(dllimport) HRESULT __stdcall DwmExtendFrameIntoClientArea(HWND hWnd, const MARGINS* pMarInset); } // NOTE: um/wininet.h ////////////////////////////////////////////////////////////////////////// @@ -1363,6 +1520,14 @@ #define EXCEPTION_CONTINUE_SEARCH 0 #define EXCEPTION_CONTINUE_EXECUTION (-1) + // NOTE: um/shellscalingapi.h + typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI + } MONITOR_DPI_TYPE; + DN_MSVC_WARNING_POP #endif // !defined(_INC_WINDOWS) #endif // !defined(DN_OS_WINDOWS_H) diff --git a/Source/Standalone/dn_sha3.h b/Source/Standalone/dn_sha3.h index 509aa4f..99c1ac3 100644 --- a/Source/Standalone/dn_sha3.h +++ b/Source/Standalone/dn_sha3.h @@ -800,7 +800,7 @@ DN_TestCore DN_SHA3_TestSuite(DN_Arena *arena) void DN_SHA3_TestSuiteThenOutput(DN_Str8FromTestCoreFlags flags) { - DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitDefault()); + DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitDefault(), "DN SHA3 Tests"); DN_TestCore test = DN_SHA3_TestSuite(&arena); DN_Str8 output = DN_Str8FromTestCore(&test, &arena, flags); printf("%.*s", DN_Str8PrintFmt(output)); diff --git a/Source/dn.cpp b/Source/dn.cpp index bb7e9dd..511493a 100644 --- a/Source/dn.cpp +++ b/Source/dn.cpp @@ -51,8 +51,15 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) } { - os->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(4), DN_MemFlags_NoAllocTrack, DN_OS_HeapInitDefault()); - os->arena = DN_ArenaFromMemList(&os->mem); + // NOTE: We initially exclude the OS allocation from the mem debugger because when we allocate + // memory, it hits the debug hook and tries to register the allocation. The memory debugger + // on `DN_OS_W32Init` to create stack traces which has not run yet. So we exclude it for now + // and add it into the memory debugger manually after the fact. + DN_Heap heap = DN_OS_HeapInitDefault(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + + os->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(4), DN_MemFlags_Nil, heap, "DN OS MemList"); + os->arena = DN_ArenaFromMemList(&os->mem); #if defined(DN_PLATFORM_WIN32) os->platform_context = DN_ArenaNew(&os->arena, DN_OSW32Core, DN_ZMem_Yes); @@ -61,28 +68,30 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) #endif #if defined(DN_PLATFORM_WIN32) - DN_OSW32Core *w32 = DN_Cast(DN_OSW32Core *) os->platform_context; - InitializeCriticalSection(&w32->sync_primitive_free_list_mutex); - - QueryPerformanceFrequency(&w32->qpc_frequency); - HMODULE module = LoadLibraryA("kernel32.dll"); - if (module) { - w32->set_thread_description = DN_Cast(DN_OSW32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription"); - FreeLibrary(module); - } - - // NOTE: win32 bcrypt - wchar_t const BCRYPT_ALGORITHM[] = L"RNG"; - long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/); - if (w32->bcrypt_rng_handle && init_status == 0) - w32->bcrypt_init_success = true; - else - DN_LogErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status); + DN_OS_W32Init(DN_Cast(DN_OSW32Core *) os->platform_context); #else DN_OS_PosixInit(DN_Cast(DN_OSPosixCore *)os->platform_context); #endif } + // NOTE: Mem Debugger + { + #if DN_MEM_DEBUGGER + // NOTE: Setup the mem debugger table with allocation tracking turned off on because we're + // allocating before we've setup the table to track allocations itself (and would otherwise cause an recursive loop). + DN_Heap heap = DN_OS_HeapInitBasic(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + DN_CallSite alloc_call_site = DN_CallSiteNowNamed("DN Memory Debugger Hash Table"); + DN_HTableInitArgs table_args = DN_HTableInitArgsDefault(DN_MemDebuggerKV, dn->mem_debugger.alloc_table_kvs, hash, key, value); + dn->mem_debugger.alloc_table = DN_HTableInitHeapAssert(table_args, heap); + + // NOTE: Unset the exclusion flag and manually add the pointers we allocator + os->mem.heap.flags &= ~DN_HeapFlags_ExcludeFromMemDebugger; + for (DN_MemBlock *block = os->arena.mem->curr; block; block = block->prev) + DN_MemDebuggerAlloc(&dn->mem_debugger, block, block->reserve, /*alloc_can_leak=*/true, alloc_call_site); + #endif + } + os->cpu_report = DN_CPUGetReport(); #define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_Str8Lit(#label)}; @@ -92,20 +101,12 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) #endif // NOTE: Initialise thread context - DN_TcInitFromHeap(&dn->main_tc, DN_OS_ThreadID(), args, DN_OS_HeapInitDefault()); + DN_Heap tc_heap = DN_OS_HeapInitDefault(); + tc_heap.flags |= DN_HeapFlags_MemDebuggerCanLeakPtr; + DN_TcInitFromHeap(&dn->main_tc, DN_OS_ThreadID(), args, tc_heap); DN_TcEquip(&dn->main_tc); } - if (DN_BitIsSet(flags, DN_InitFlags_LeakTracker)) { - DN_Assert(dn->os_init); - #if DN_WITH_OS - // NOTE: Setup the allocation table with allocation tracking turned off on - // the arena we're using to initialise the table. - DN_HTableInitArgs table_args = DN_HTableInitArgsDefault(DN_LeakTrackerKV, dn->leak.alloc_table_kvs, hash, key, value); - dn->leak.alloc_table = DN_HTableInitHeapAssert(table_args, DN_OS_HeapInitBasic()); - #endif - } - // NOTE: Print out init features char buf[4096]; DN_USize buf_size = 0; @@ -156,7 +157,7 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args) } #endif - #if defined(DN_LEAK_TRACKING) + #if defined(DN_MEM_DEBUGGER) DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " Allocation leak tracing\n"); #endif @@ -1077,7 +1078,7 @@ DN_API DN_F32 DN_EpsilonClampF32(DN_F32 value, DN_F32 target, DN_F32 epsilon) return result; } -static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_Heap heap) +static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, DN_Heap heap, DN_CallSite call_site) { DN_MemBlock *result = nullptr; switch (heap.type) { @@ -1085,14 +1086,18 @@ static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool tra break; case DN_HeapType_Basic: { - DN_AssertF(reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", reserve, DN_ARENA_HEADER_SIZE); - result = DN_Cast(DN_MemBlock *) heap.basic_alloc(reserve); + DN_USize real_reserve = reserve; + if (reserve == 0) + real_reserve = DN_ARENA_RESERVE_SIZE; + + DN_AssertF(real_reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", real_reserve, DN_ARENA_HEADER_SIZE); + result = DN_Cast(DN_MemBlock *) DN_HeapAllocCallSite(&heap, real_reserve, 0, DN_HeapAllocFlag_Nil, call_site); if (!result) return result; result->used = DN_ARENA_HEADER_SIZE; - result->commit = reserve; - result->reserve = reserve; + result->commit = real_reserve; + result->reserve = real_reserve; } break; case DN_HeapType_Virtual: { @@ -1109,25 +1114,15 @@ static DN_MemBlock *DN_MemBlockFromHeap_(DN_U64 reserve, DN_U64 commit, bool tra real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve); DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%zu < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve); - DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No; - result = DN_Cast(DN_MemBlock *) heap.virtual_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite); - if (!result) - return result; - - if (mem_commit == DN_MemCommit_No && !heap.virtual_commit(result, real_commit, DN_MemPage_ReadWrite)) { - heap.virtual_release(result, real_reserve); - return result; + result = DN_Cast(DN_MemBlock *) DN_HeapAllocCallSite(&heap, real_reserve, real_commit, DN_HeapAllocFlag_Nil, call_site); + if (result) { + result->used = DN_ARENA_HEADER_SIZE; + result->commit = real_commit; + result->reserve = real_reserve; } - - result->used = DN_ARENA_HEADER_SIZE; - result->commit = real_commit; - result->reserve = real_reserve; } break; } - if (track_alloc && result) - DN_LeakTrackAlloc(&g_dn_->leak, result, result->reserve, alloc_can_leak); - return result; } @@ -1140,11 +1135,9 @@ static bool DN_ArenaHasPoison_(DN_MemFlags flags) return result; } -static DN_MemBlock *DN_MemBlockFromHeapFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +static DN_MemBlock *DN_MemBlockFromHeapFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { - bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; - bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; - DN_MemBlock *result = DN_MemBlockFromHeap_(reserve, commit, track_alloc, alloc_can_leak, heap); + DN_MemBlock *result = DN_MemBlockFromHeap_(reserve, commit, heap, call_site); if (result && DN_ArenaHasPoison_(flags)) { char *poison = DN_Cast(char *)result + result->used; DN_USize poison_size = result->commit - result->used; @@ -1214,43 +1207,46 @@ DN_API DN_Heap DN_HeapInitVirtual(DN_U32 page_size, DN_HeapVirtualReserveFunc *v return result; } -DN_API void* DN_HeapAlloc(DN_Heap *heap, DN_USize reserve, DN_USize commit) +DN_API void *DN_HeapAllocCallSite(DN_Heap *heap, DN_USize reserve, DN_USize commit, DN_HeapAllocFlag flags, DN_CallSite call_site) { void *result = nullptr; switch (heap->type) { case DN_HeapType_Nil: break; - case DN_HeapType_Basic: result = heap->basic_alloc(reserve); break; + case DN_HeapType_Basic: { + result = heap->basic_alloc(reserve); + } break; case DN_HeapType_Virtual: { - DN_AssertF(heap->virtual_page_size, - "Page size must be set to a non-zero, power of two value. Virtual memory " - "functions are usually initialised by values obtained during initialisation, has " - "DN_Init() been called yet?"); - DN_Assert(DN_IsPowerOfTwo(heap->virtual_page_size)); + DN_MemCommit mem_commit = reserve == commit ? DN_MemCommit_Yes : DN_MemCommit_No; + result = DN_Cast(DN_MemBlock *) heap->virtual_reserve(reserve, mem_commit, DN_MemPage_ReadWrite); + if (result) { + bool commit_success = true; + if (mem_commit == DN_MemCommit_No && commit > 0) { + // NOTE: If mem-commit is yes, then the entire block of memory that was reserved is + // committed in the same syscall as the allocation itself as an optimisation. If + // mem-commit is no, which is this branch, then we commit just the range that the user + // requested in `commit`. + commit_success = heap->virtual_commit(result, commit, DN_MemPage_ReadWrite); + } - DN_USize const page_size = heap->virtual_page_size; - DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE; - DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE; - real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size); - real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve); - DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%zu < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve); - - DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No; - result = DN_Cast(DN_MemBlock *) heap->virtual_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite); - if (!result) - return result; - - if (mem_commit == DN_MemCommit_No && !heap->virtual_commit(result, real_commit, DN_MemPage_ReadWrite)) { - heap->virtual_release(result, real_reserve); - return result; + if (!commit_success) { + heap->virtual_release(result, reserve); + result = nullptr; + } } } break; } if (result) { - heap->bytes_alloc += reserve; + heap->bytes_alloc += reserve; heap->bytes_alloc_total += reserve; + if (DN_BitIsNotSet(flags, DN_HeapAllocFlag_MemDebuggerExcludePtr) && + DN_BitIsNotSet(heap->flags, DN_HeapFlags_ExcludeFromMemDebugger)) { + (void)call_site; + bool ptr_can_leak = flags & DN_HeapAllocFlag_MemDebuggerCanLeakPtr || heap->flags & DN_HeapFlags_MemDebuggerCanLeakPtr; + DN_MemDebuggerAlloc(&g_dn_->mem_debugger, result, reserve, ptr_can_leak, call_site); + } } return result; } @@ -1259,6 +1255,10 @@ DN_API void DN_HeapDealloc(DN_Heap *heap, void *ptr, DN_USize size) { heap->bytes_alloc -= size; heap->bytes_freed += size; + + if (DN_BitIsNotSet(heap->flags, DN_HeapFlags_ExcludeFromMemDebugger)) + DN_MemDebuggerDealloc(&g_dn_->mem_debugger, ptr); + if (heap->type == DN_HeapType_Basic) heap->basic_dealloc(ptr); else @@ -1280,18 +1280,18 @@ DN_API DN_MemList DN_MemListFromBuffer(void *buffer, DN_USize size, DN_MemFlags DN_ASanPoisonMemoryRegion(DN_Cast(char *) block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE); DN_MemList result = {}; - result.flags = flags | DN_MemFlags_NoGrow | DN_MemFlags_NoAllocTrack | DN_MemFlags_AllocCanLeak | DN_MemFlags_UserBuffer; + result.flags = flags | DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer; result.curr = block; DN_MemListOnNewBlock_(&result, result.curr); return result; } -DN_API DN_MemList DN_MemListFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +DN_API DN_MemList DN_MemListFromHeapCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { DN_MemList result = {}; result.heap = heap; result.flags |= flags | DN_MemFlags_Heap; - result.curr = DN_MemBlockFromHeapFlags_(reserve, commit, flags, heap); + result.curr = DN_MemBlockFromHeapFlags_(reserve, commit, flags, heap, call_site); DN_MemListOnNewBlock_(&result, result.curr); return result; } @@ -1299,12 +1299,8 @@ DN_API DN_MemList DN_MemListFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags static void DN_MemBlockDeinit_(DN_MemList *mem, DN_MemBlock *block) { DN_USize release_size = block->reserve; - if (DN_BitIsNotSet(mem->flags, DN_MemFlags_NoAllocTrack)) - DN_LeakTrackDealloc(&g_dn_->leak, block); - if (DN_ArenaHasPoison_(mem->flags)) DN_ASanUnpoisonMemoryRegion(block, block->commit); - if (mem->flags & DN_MemFlags_Heap) DN_HeapDealloc(&mem->heap, block, release_size); } @@ -1367,8 +1363,8 @@ DN_API bool DN_MemListGrow(DN_MemList *mem, DN_U64 reserve, DN_U64 commit) if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) return false; - bool result = false; - DN_MemBlock *new_block = DN_MemBlockFromHeapFlags_(reserve, commit, mem->flags, mem->heap); + bool result = false; + DN_MemBlock *new_block = DN_MemBlockFromHeapFlags_(reserve, commit, mem->flags, mem->heap, DN_CallSiteNowNamed("DN MemList Grow")); if (new_block) { result = true; new_block->prev = mem->curr; @@ -1385,7 +1381,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem return nullptr; if (!mem->curr) { - mem->curr = DN_MemBlockFromHeapFlags_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, mem->flags, mem->heap); + mem->curr = DN_MemBlockFromHeapFlags_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, mem->flags, mem->heap, DN_CallSiteNowNamed("DN MemList Lazy Bootstrap")); DN_MemListOnNewBlock_(mem, mem->curr); } @@ -1687,9 +1683,9 @@ DN_API DN_Arena DN_ArenaFromMemList(DN_MemList *mem) return result; } -DN_API DN_Arena DN_ArenaFromHeap(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap) +DN_API DN_Arena DN_ArenaFromHeapCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site) { - DN_MemList mem = DN_MemListFromHeap(reserve, commit, flags, heap); + DN_MemList mem = DN_MemListFromHeapCallSite(reserve, commit, flags, heap, call_site); DN_Arena result = {}; result.flags |= DN_ArenaFlags_OwnsMemList; result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); @@ -2175,19 +2171,19 @@ DN_API DN_TcInitArgs DN_TcInitArgsDefault() DN_API void DN_TcInitFromHeap(DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap) { DN_Assert(args.temp_count <= DN_ArrayCountU(tc->temp_arenas)); - DN_MemList main_mem_stack = DN_MemListFromHeap(args.main_reserve, args.main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList main_mem_stack = DN_MemListFromHeap(args.main_reserve, args.main_commit, DN_MemFlags_Nil, heap, "DN Thread Context Main MemList"); DN_MemList *main_mem = DN_MemListNewCopy(&main_mem_stack, DN_MemList, &main_mem_stack); DN_Arena *main_arena = DN_MemListNewZ(main_mem, DN_Arena); *main_arena = DN_ArenaFromMemList(main_mem); DN_Arena *temp_arenas = DN_MemListNewArrayZ(main_mem, DN_Arena, args.temp_count); for (DN_ForIndexU(index, args.temp_count)) { - DN_MemList temp_mem_stack = DN_MemListFromHeap(args.temp_reserve, args.temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList temp_mem_stack = DN_MemListFromHeap(args.temp_reserve, args.temp_commit, DN_MemFlags_Nil, heap, "DN Thread Context Temp MemList"); DN_MemList *temp_mem = DN_MemListNewCopy(main_mem, DN_MemList, &temp_mem_stack); temp_arenas[index] = DN_ArenaFromMemList(temp_mem); } - DN_MemList err_sink_mem_stack = DN_MemListFromHeap(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, heap); + DN_MemList err_sink_mem_stack = DN_MemListFromHeap(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_Nil, heap, "DN ThreadContext Err MemList"); DN_MemList *err_sink_mem = DN_MemListNewCopy(main_mem, DN_MemList, &err_sink_mem_stack); DN_Arena *err_sink_arena = DN_MemListNewZ(main_mem, DN_Arena); *err_sink_arena = DN_ArenaFromMemList(err_sink_mem); @@ -5300,11 +5296,12 @@ DN_API bool DN_Utf32FromUtf8Iterate(DN_Utf32FromUtf8Iterator *it, DN_Utf32Iterat it->remaining = utf8; it->init = true; } - DN_Utf32FromResult decode = DN_Utf32FromUtf8Stream(it->remaining); - it->success = decode.success; - it->remaining = decode.remaining; - it->codepoint = decode.codepoint; - it->bytes_decoded += decode.byte_count; + DN_Utf32FromResult decode = DN_Utf32FromUtf8Stream(it->remaining); + it->success = decode.success; + it->remaining = decode.remaining; + it->codepoint = decode.codepoint; + it->bytes_decoded_ = decode.byte_count; + it->bytes_decoded_total += decode.byte_count; } // NOTE: Or otherwise iterate backwards @@ -5321,10 +5318,11 @@ DN_API bool DN_Utf32FromUtf8Iterate(DN_Utf32FromUtf8Iterator *it, DN_Utf32Iterat DN_Utf32FromResult utf32_from = DN_Utf32FromUtf8Stream(utf8_stream); if (utf32_from.success) { DN_Assert(utf32_from.byte_count <= it->remaining.count); - it->success = true; - it->remaining = DN_Str8FromPtr(it->remaining.data, it->remaining.count - utf32_from.byte_count); - it->bytes_decoded += utf32_from.byte_count; - it->codepoint = utf32_from.codepoint; + it->success = true; + it->remaining = DN_Str8FromPtr(it->remaining.data, it->remaining.count - utf32_from.byte_count); + it->bytes_decoded_ = utf32_from.byte_count; + it->bytes_decoded_total += utf32_from.byte_count; + it->codepoint = utf32_from.codepoint; break; } } @@ -5803,8 +5801,8 @@ DN_API DN_Profiler DN_ProfilerInit(DN_ProfilerAnchor *anchors, DN_USize count, D "frequency of rdtsc yourself. The reason for this is that measuring rdtsc requires " "having some alternate timing mechanism to measure the duration between the Tscs " "provided by rdtsc and this profiler makes no assumption about what timing primitives " - "are available other than rdtsc which is a CPU builtin available on basically all " - "platforms or have an equivalent (e.g. __builtin_readcyclecounter)" + "that are available other than rdtsc which is a CPU builtin available on basically " + "all platforms or have an equivalent (e.g. __builtin_readcyclecounter)" "" "This codebase provides DN_OS_EstimateTscPerSecond() as an example of how to that for " "convenience and is available if compiling with the OS layer. Some platforms like " @@ -5973,6 +5971,291 @@ DN_API DN_F64 DN_ProfilerMsFromTsc(DN_Profiler *profiler, DN_U64 duration_tsc) return result; } +DN_API DN_Profiler2 DN_Profiler2Init(DN_ProfilerTscNowFunc *tsc_now_func, DN_U64 tsc_frequency, DN_Profiler2Marker *markers, DN_U32 markers_count, DN_Profiler2Frame *frames, DN_U32 frames_count) +{ + DN_Profiler2 result = {}; + result.tsc_now = tsc_now_func; + result.tsc_frequency = tsc_frequency; + result.frames = frames; + result.frames_max = frames_count; + result.markers = markers; + result.markers_max = markers_count; + return result; +} + +DN_API void DN_Profiler2FrameBegin(DN_Profiler2 *profiler) +{ + profiler->frame_num++; + + if (profiler->frames_max == 0) + return; + if (profiler->markers_max == 0) + return; + + // NOTE: Mid-frame someone paused the profiler. We will discard this frame's profiling data + // because the frame timestamp counter is going to account for the pause time which is going to + // spike the results unnecessarily. + if (profiler->state == DN_Profiler2State_UnpauseEnqueued) { + // NOTE: Discard the frame's data + profiler->active_frame = nullptr; + profiler->frame_begin_zone = {}; + if (profiler->frames_write_index_unwrapped) + profiler->frames_write_index_unwrapped -= 1; // Allow re-use of the frame + + // NOTE: Eat the unpause state, and transition into running state + profiler->state = DN_Profiler2State_Run; + } + + if (profiler->state != DN_Profiler2State_Run) + return; + + // NOTE: Reset the active parent + profiler->active_parent_index = 0; + + // NOTE: Calling frame-begin itself starts a profiling zone that encompasses the elapsed time + // between frame-begin calls (in an interactive application this would be the duration of + // essentially update loop). First we end the prior zone if it exists. + DN_U64 prev_frame_zone_tsc_end = 0; + if (profiler->frame_begin_zone.marker) { + prev_frame_zone_tsc_end = profiler->frame_begin_zone.marker->tsc_end; + DN_Profiler2ZoneEnd(&profiler->frame_begin_zone); + } + + // NOTE: Grab a copy of the previous frame + DN_Profiler2Frame prev_frame = {}; + if (profiler->frames_write_index_unwrapped > 0) { + DN_USize prev_frame_index = (profiler->frames_write_index_unwrapped - 1) % profiler->frames_max; + prev_frame = profiler->frames[prev_frame_index]; + } + + // NOTE: Grab a copy of the oldest frame (essentially the frame we're going to write into if + // we've wrapped, otherwise no-op here). We copy it out for clarity of semantics because we're + // about to write to the oldest frame in the scenario that we've already wrapped all the frames. + DN_USize frames_write_index = profiler->frames_write_index_unwrapped % profiler->frames_max; + DN_Profiler2Frame oldest_frame = {}; + if (profiler->frames_write_index_unwrapped >= profiler->frames_max) + oldest_frame = profiler->frames[frames_write_index]; + + // NOTE: Calculate the range into `profiler->markers` that the previous frame used + DN_V2USize prev_offset = {}; + if (profiler->frames_write_index_unwrapped > 0) + prev_offset = DN_Profiler2MarkerOffsetFromFrame(profiler, &prev_frame); + + DN_Profiler2Frame *frame = profiler->frames + frames_write_index; + if (profiler->frames_write_index_unwrapped < profiler->frames_max) { + // NOTE: If we haven't wrapped frames yet, the maximum allowed markers is exactly the + // remaining markers that haven't been used in the markers array because there's no frames to + // "reclaim" markers from yet. + frame->max = profiler->markers_max - DN_Cast(DN_U32)prev_offset.end; + } else { + // NOTE: Suppose markers_max = 100, frames_max = 3 + // + // Frame 0: begin=0, end=10 (oldest, being evicted) + // Frame 1: begin=10, end=25 (live) + // Frame 2: begin=25, end=40 (prev, live) + // + // On frame 3 we are going to overwrite frame 0. The range of markers we can write becomes + // markers [40, 100] and [0, 10]. We can calculate this delta as being + // `oldest.end - prev.end`. This calculation works if `oldest.end > prev.end` but we can make + // it work universally by adding `markers_max` to ensure it's positive for the modulo. + // + // The biggest delta that `oldest.end - prev.end` can produce is exactly `markers_max` and the + // modulo wraps everything back into the proper range. + DN_V2USize oldest_offset = DN_Profiler2MarkerOffsetFromFrame(profiler, &oldest_frame); + DN_U32 delta = DN_Cast(DN_U32)(oldest_offset.end - prev_offset.end); + frame->max = (delta + profiler->markers_max) % profiler->markers_max; + } + + frame->begin_offset = DN_Cast(DN_U32)prev_offset.end; + frame->count = DN_Min(1, frame->max); // Reserve the 0th marker for the nil-marker if possible + + // NOTE: Zero out the markers for this frame, do this upfront so that we don't memset inside the + // zone. The markers ring buffer may wrap around the end of the global markers array so we may + // need two memsets. + if (frame->begin_offset + frame->max > profiler->markers_max) { + DN_USize first_count = profiler->markers_max - frame->begin_offset; + DN_USize second_count = frame->max - first_count; + DN_Memset(profiler->markers + frame->begin_offset, 0, sizeof(DN_Profiler2Marker) * first_count); + DN_Memset(profiler->markers, 0, sizeof(DN_Profiler2Marker) * second_count); + } else { + DN_Memset(profiler->markers + frame->begin_offset, 0, sizeof(DN_Profiler2Marker) * frame->max); + } + + profiler->active_frame = frame; + profiler->active_frame->frame_num = profiler->frame_num - 1; + profiler->frames_write_index_unwrapped++; + + // NOTE: Start a new profiling zone once the frame has been setup. We do a small trick and set + // the begin TSC to the previous frame (if it exists) so that we capture all the time that has + // elapsed since ending the previous frame. We can't immediately update the zone because we need + // to setup the frame/markers. + profiler->frame_begin_zone = DN_Profiler2ZoneBeginName(profiler, "Profiler Frame"); + profiler->active_frame->begin_marker = profiler->frame_begin_zone.marker; + if (prev_frame_zone_tsc_end) + profiler->active_frame->begin_marker->tsc_begin = prev_frame_zone_tsc_end; +} + +static bool DN_Profiler2FunctionTimingLessThan_(void const *lhs, void const *rhs, void *) +{ + DN_Profiler2FunctionTime const *lhs_timing = DN_Cast(DN_Profiler2FunctionTime const *)lhs; + DN_Profiler2FunctionTime const *rhs_timing = DN_Cast(DN_Profiler2FunctionTime const *)rhs; + DN_Str8 lhs_file = lhs_timing->call_site.file; + DN_Str8 rhs_file = rhs_timing->call_site.file; + bool result = false; + int cmp = DN_Memcmp(lhs_file.data, rhs_file.data, DN_Min(lhs_file.count, rhs_file.count)); + if (cmp == 0 && lhs_file.count == rhs_file.count) + result = lhs_timing->call_site.line < rhs_timing->call_site.line; + else if (cmp == 0) + result = lhs_file.count < rhs_file.count; + else + result = cmp < 0; + return result; +} + +DN_API DN_Profiler2FunctionTimes DN_Profiler2FunctionTimesFromFrame(DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame, DN_Arena *arena) +{ + DN_Profiler2FunctionTimes result = {}; + if (!frame) + return result; + + DN_PArrayPrepareArena(result.data, result.count, &result.max, arena, 128); + for (DN_ForIndexU(index, frame->count)) { + if (index == 0) + continue; + DN_Profiler2Marker* marker = DN_Profiler2MarkerFromFrameIndex(DN_Cast(DN_Profiler2 *)profiler, DN_Cast(DN_Profiler2Frame *)frame, DN_Cast(DN_U32)index); + DN_Profiler2FunctionTime find = {}; + find.call_site = marker->call_site; + + DN_Profiler2FunctionTime *timing = nullptr; + DN_BSearchResult search = DN_BSearch(result.data, result.count, sizeof(*result.data), &find, DN_BSearchType_Match, nullptr, DN_Profiler2FunctionTimingLessThan_); + if (search.found) + timing = result.data + search.index; + else + timing = DN_PArrayInsert(result.data, &result.count, result.max, search.index, find); + + timing->name = marker->name; + timing->hit_count++; + timing->tsc_total += marker->tsc_end - marker->tsc_begin; + timing->tsc_children_total += marker->tsc_children_duration; + } + return result; +} + +DN_API DN_Profiler2Zone DN_Profiler2ZoneBegin(DN_Profiler2 *profiler, DN_Str8 name, DN_CallSite call_site) +{ + DN_Profiler2Zone result = {}; + DN_Profiler2Frame *frame = profiler && profiler->state == DN_Profiler2State_Run ? profiler->active_frame : nullptr; + if (frame) { + if (frame->count < frame->max) { + result.marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, frame->count); + frame->count++; + } + } + + if (result.marker) { + DN_U32 marker_index = frame->count - 1; + DN_Profiler2Marker *marker = result.marker; + marker->call_site = call_site; + marker->tsc_begin = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTsc(); + marker->parent_index = profiler->active_parent_index; + marker->name = name; + + // NOTE: Attach the zone to the parent + DN_Profiler2Marker *parent = DN_Profiler2MarkerFromFrameIndex(profiler, frame, marker->parent_index); + if (parent->first_child_index == 0) { + parent->first_child_index = marker_index; + DN_Assert(parent->last_child_index == 0); + } + + // NOTE: Unconditionally update the parent's last child, always valid even if last child points to + // the zeroth item since that's the nil-item, we can write garbage because nothing's assigned to + // it. + DN_Profiler2Marker *last_child = DN_Profiler2MarkerFromFrameIndex(profiler, frame, parent->last_child_index); + last_child->next_index = marker_index; + parent->last_child_index = marker_index; + + // NOTE: Set this zone as the active zone that nested zone should attach to + profiler->active_parent_index = marker_index; + result.profiler = profiler; + } + return result; +} + +DN_API void DN_Profiler2ZoneEnd(DN_Profiler2Zone *zone) +{ + zone->scope_once = true; + if (!zone->marker) + return; + + DN_Assert(!zone->ended); + zone->ended = true; + + // NOTE: Record the end timestamp + DN_Profiler2* profiler = zone->profiler; + zone->marker->tsc_end = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTsc(); + + // NOTE: Calculate the total time consumed into the parent + if (profiler->frame_begin_zone.marker != zone->marker) { + DN_Profiler2Marker* parent = DN_Profiler2MarkerFromFrameIndex(profiler, profiler->active_frame, zone->marker->parent_index); + parent->tsc_children_duration += zone->marker->tsc_end - zone->marker->tsc_begin; + } + + // NOTE: Pop the active parent from the profiler + profiler->active_parent_index = zone->marker->parent_index; +} + +DN_API bool DN_Profiler2MarkerWalkDfsPreOrder(DN_Profiler2 *profiler, DN_Profiler2Iterator *it) +{ + if (!profiler || !it || profiler->active_frame->count <= 1) + return false; + + DN_Profiler2Frame *frame = profiler->active_frame; + if (!it->init) { + it->init = true; + it->marker_index = 1; + it->iteration_count = 1; + it->last_visited_index = 0; + it->marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, 1); + return true; + } + + it->last_visited_index = it->marker_index; + it->iteration_count++; + + DN_Profiler2Marker *marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + if (marker->first_child_index != 0) { + it->marker_index = marker->first_child_index; + } else { + for (;;) { + if (marker->next_index != 0) { + it->marker_index = marker->next_index; + break; + } + if (marker->parent_index == 0) + return false; + it->marker_index = marker->parent_index; + marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + } + } + + it->marker = DN_Profiler2MarkerFromFrameIndex(profiler, frame, it->marker_index); + return true; +} + +DN_API DN_V2USize DN_Profiler2MarkerOffsetFromFrame(DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame) +{ + DN_V2USize result = {}; + result.begin = frame->begin_offset; + result.end = (result.begin + frame->count) % profiler->markers_max; + return result; +} + +DN_API DN_Profiler2Marker *DN_Profiler2MarkerFromFrameIndex(DN_Profiler2 *profiler, DN_Profiler2Frame *frame, DN_U32 index) +{ + DN_Profiler2Marker *result = profiler->markers + ((frame->begin_offset + index) % profiler->markers_max); + return result; +} + static void DN_QSortSetElem_(void *array, DN_USize elem_size, DN_USize dest_index, DN_USize src_index) { char *src = DN_Cast(char *) array + (src_index * elem_size); @@ -6340,230 +6623,310 @@ DN_API DN_U64 DN_Fnv1aHashU64FromBytes(void const *bytes, DN_USize count, DN_U64 return hash; } -#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL) - #define DN_MMH3_ROTL32(x, y) _rotl(x, y) - #define DN_MMH3_ROTL64(x, y) _rotl64(x, y) -#else - #define DN_MMH3_ROTL32(x, y) ((x) << (y)) | ((x) >> (32 - (y))) - #define DN_MMH3_ROTL64(x, y) ((x) << (y)) | ((x) >> (64 - (y))) +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/*----------------------------------------------------------------------------- +If you want to understand the MurmurHash algorithm you would be much better +off reading the original source. Just point your browser at: +http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + + +What this version provides? + +1. Progressive data feeding. Useful when the entire payload to be hashed +does not fit in memory or when the data is streamed through the application. +Also useful when hashing a number of strings with a common prefix. A partial +hash of a prefix string can be generated and reused for each suffix string. + +2. Portability. Plain old C so that it should compile on any old compiler. +Both CPU endian and access-alignment neutral, but avoiding inefficient code +when possible depending on CPU capabilities. + +3. Drop in. I personally like nice self contained public domain code, making it +easy to pilfer without loads of refactoring to work properly in the existing +application code & makefile structure and mucking around with licence files. +Just copy PMurHash.h and PMurHash.c and you're ready to go. + + +How does it work? + +We can only process entire 32 bit chunks of input, except for the very end +that may be shorter. So along with the partial hash we need to give back to +the caller a carry containing up to 3 bytes that we were unable to process. +This carry also needs to record the number of bytes the carry holds. I use +the low 2 bits as a count (0..3) and the carry bytes are shifted into the +high byte in stream order. + +To handle endianess I simply use a macro that reads a uint32_t and define +that macro to be a direct read on little endian machines, a read and swap +on big endian machines, or a byte-by-byte read if the endianess is unknown. + +-----------------------------------------------------------------------------*/ +/* MSVC warnings we choose to ignore */ +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4127) /* conditional expression is constant */ #endif -//----------------------------------------------------------------------------- -// Block read - if your platform needs to do endian-swapping or can only -// handle aligned reads, do the conversion here -DN_FORCE_INLINE DN_U32 DN_Murmur3GetBlock32_(DN_U32 const *p, int i) -{ - return p[i]; -} +/*----------------------------------------------------------------------------- + * Endianess, misalignment capabilities and util macros + * + * The following 3 macros are defined in this section. The other macros defined + * are only needed to help derive these 3. + * + * DN_M3H_READ_UINT32(x) Read a little endian unsigned 32-bit int + * DN_M3H_UNALIGNED_SAFE Defined if DN_M3H_READ_UINT32 works on non-word boundaries + * DN_M3H_ROTL32(x,r) Rotate x left by r bits + */ -DN_FORCE_INLINE DN_U64 DN_Murmur3GetBlock64_(DN_U64 const *p, int i) -{ - return p[i]; -} +/* Convention is to define __BYTE_ORDER == to one of these values */ +#if !defined(DN_M3H__BIG_ENDIAN) + #define DN_M3H__BIG_ENDIAN 4321 +#endif +#if !defined(DN_M3H__LITTLE_ENDIAN) + #define DN_M3H__LITTLE_ENDIAN 1234 +#endif -//----------------------------------------------------------------------------- -// Finalization mix - force all bits of a hash block to avalanche +/* I386 */ +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386) + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #define DN_M3H_UNALIGNED_SAFE +#endif -DN_FORCE_INLINE DN_U32 DN_Murmur3FMix32_(DN_U32 h) +/* gcc 'may' define DN_M3H__LITTLE_ENDIAN__ or DN_M3H__BIG_ENDIAN__ to 1 (Note the trailing __), + * or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */ +#if !defined(DN_M3H__BYTE_ORDER) + #if defined(DN_M3H__LITTLE_ENDIAN__) && DN_M3H__LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1 + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #elif defined(DN_M3H__BIG_ENDIAN__) && DN_M3H__BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1 + #define DN_M3H__BYTE_ORDER DN_M3H__BIG_ENDIAN + #endif +#endif + +/* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */ +#if !defined(DN_M3H__BYTE_ORDER) + #if defined(__ARMEL__) || defined(__MIPSEL__) + #define DN_M3H__BYTE_ORDER DN_M3H__LITTLE_ENDIAN + #endif + #if defined(__ARMEB__) || defined(__MIPSEB__) + #define DN_M3H__BYTE_ORDER DN_M3H__BIG_ENDIAN + #endif +#endif + +/* Now find best way we can to DN_M3H_READ_UINT32 */ +#if DN_M3H__BYTE_ORDER==DN_M3H__LITTLE_ENDIAN + /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ + #define DN_M3H_READ_UINT32(ptr) (*((DN_U32*)(ptr))) +#elif DN_M3H__BYTE_ORDER==DN_M3H__BIG_ENDIAN + /* TODO: Add additional cases below where a compiler provided bswap32 is available */ + #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) + #define DN_M3H_READ_UINT32(ptr) (__builtin_bswap32(*((DN_U32*)(ptr)))) + #else + /* Without a known fast bswap32 we're just as well off doing this */ + #define DN_M3H_READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + #define DN_M3H_UNALIGNED_SAFE + #endif +#else + /* Unknown endianess so last resort is to read individual bytes */ + #define DN_M3H_READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + + /* Since we're not doing word-reads we can skip the messing about with realignment */ + #define DN_M3H_UNALIGNED_SAFE +#endif + +/* Find best way to ROTL32 */ +#if defined(_MSC_VER) + #include /* Microsoft put _rotl declaration in here */ + #define DN_M3H_ROTL32(x,r) _rotl(x,r) +#else + /* gcc recognises this code and generates a rotate instruction for CPUs with one */ + #define DN_M3H_ROTL32(x,r) (((DN_U32)x << r) | ((DN_U32)x >> (32 - r))) +#endif + + +/*----------------------------------------------------------------------------- + * Core murmurhash algorithm macros */ + +#define DN_M3H_C1 (0xcc9e2d51) +#define DN_M3H_C2 (0x1b873593) + +/* This is the main processing body of the algorithm. It operates + * on each full 32-bits of input. */ +#define DN_M3H_DOBLOCK(h1, k1) do{ \ + k1 *= DN_M3H_C1; \ + k1 = DN_M3H_ROTL32(k1,15); \ + k1 *= DN_M3H_C2; \ + \ + h1 ^= k1; \ + h1 = DN_M3H_ROTL32(h1,13); \ + h1 = h1*5+0xe6546b64; \ + }while(0) + +/* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ +/* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ +#define DN_M3H_DOBYTES(cnt, h1, c, n, ptr, len) do{ \ + int _i = cnt; \ + while(_i--) { \ + c = c>>8 | *ptr++<<24; \ + n++; len--; \ + if(n==4) { \ + DN_M3H_DOBLOCK(h1, c); \ + n = 0; \ + } \ + } }while(0) + +/*---------------------------------------------------------------------------*/ + +/* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed + * if wanted. Both ph1 and pcarry are required arguments. */ +DN_API void DN_Murmur3HashRawProcess(DN_U32 *ph1, DN_U32 *pcarry, const void *key, int len) { + DN_U32 h1 = *ph1; + DN_U32 c = *pcarry; + + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end; + + /* Extract carry count from low 2 bits of c value */ + int n = c & 3; + +#if defined(DN_M3H_UNALIGNED_SAFE) + /* This CPU handles unaligned word access */ + + /* Consume any carry bytes */ + int i = (4-n) & 3; + if(i && i <= len) { + DN_M3H_DOBYTES(i, h1, c, n, ptr, len); + } + + /* Process 32-bit chunks */ + end = ptr + len/4*4; + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = DN_M3H_READ_UINT32(ptr); + DN_M3H_DOBLOCK(h1, k1); + } + +#else /*DN_M3H_UNALIGNED_SAFE*/ + /* This CPU does not handle unaligned word access */ + + /* Consume enough so that the next data byte is word aligned */ + int i = -(long)ptr & 3; + if(i && i <= len) { + DN_M3H_DOBYTES(i, h1, c, n, ptr, len); + } + + /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ + end = ptr + len/4*4; + switch(n) { /* how many bytes in c */ + case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = DN_M3H_READ_UINT32(ptr); + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>24; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<8; + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>16; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<16; + DN_M3H_DOBLOCK(h1, k1); + } + break; + case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ + for( ; ptr < end ; ptr+=4) { + DN_U32 k1 = c>>8; + c = DN_M3H_READ_UINT32(ptr); + k1 |= c<<24; + DN_M3H_DOBLOCK(h1, k1); + } + } +#endif /*DN_M3H_UNALIGNED_SAFE*/ + + /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ + len -= len/4*4; + + /* Append any remaining bytes into carry */ + DN_M3H_DOBYTES(len, h1, c, n, ptr, len); + + /* Copy out new running hash and carry */ + *ph1 = h1; + *pcarry = (c & ~0xff) | n; +} + +/*---------------------------------------------------------------------------*/ + +/* Finalize a hash. To match the original Murmur3A the total_length must be provided */ +DN_API DN_U32 DN_Murmur3HashRawResult(DN_U32 h, DN_U32 carry, DN_U32 total_length) +{ + DN_U32 k1; + int n = carry & 3; + if(n) { + k1 = carry >> (4-n)*8; + k1 *= DN_M3H_C1; k1 = DN_M3H_ROTL32(k1,15); k1 *= DN_M3H_C2; h ^= k1; + } + h ^= total_length; + + /* fmix */ h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; + return h; } -DN_FORCE_INLINE DN_U64 DN_Murmur3FMix64_(DN_U64 k) +/*---------------------------------------------------------------------------*/ + +/* Murmur3A compatable all-at-once */ +DN_API DN_U32 DN_Murmur3Hash(DN_U32 seed, const void *key, int len) { - k ^= k >> 33; - k *= 0xff51afd7ed558ccd; - k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53; - k ^= k >> 33; - return k; + DN_U32 h1=seed, carry=0; + DN_Murmur3HashRawProcess(&h1, &carry, key, len); + DN_U32 result = DN_Murmur3HashRawResult(h1, carry, len); + return result; } -DN_API DN_U32 DN_Murmur3HashU32FromBytesX86(void const *bytes, int len, DN_U32 seed) +DN_API DN_Murmur3Context DN_Murmur3HashInit(DN_U32 seed) { - const DN_U8 *data = (const DN_U8 *)bytes; - const int nblocks = len / 4; + DN_Murmur3Context result = {}; + result.it = seed; + return result; +} - DN_U32 h1 = seed; - - const DN_U32 c1 = 0xcc9e2d51; - const DN_U32 c2 = 0x1b873593; - - //---------- - // body - - const DN_U32 *blocks = (const DN_U32 *)(data + nblocks * 4); - - for (int i = -nblocks; i; i++) { - DN_U32 k1 = DN_Murmur3GetBlock32_(blocks, i); - - k1 *= c1; - k1 = DN_MMH3_ROTL32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = DN_MMH3_ROTL32(h1, 13); - h1 = h1 * 5 + 0xe6546b64; +DN_API void DN_Murmur3HashUpdate(DN_Murmur3Context *ctx, const void *key, int len) +{ + if (len > 0) { + DN_Murmur3HashRawProcess(&ctx->it, &ctx->carry, key, len); + ctx->bytes_count += len; } - - //---------- - // tail - - const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 4); - - DN_U32 k1 = 0; - - switch (len & 3) { - case 3: - k1 ^= tail[2] << 16; - case 2: - k1 ^= tail[1] << 8; - case 1: - k1 ^= tail[0]; - k1 *= c1; - k1 = DN_MMH3_ROTL32(k1, 15); - k1 *= c2; - h1 ^= k1; - }; - - //---------- - // finalization - - h1 ^= len; - - h1 = DN_Murmur3FMix32_(h1); - - return h1; } -DN_API DN_Murmur3 DN_Murmur3HashU128FromBytesX64(void const *bytes, int len, DN_U32 seed) +DN_API DN_U32 DN_Murmur3HashFinish(DN_Murmur3Context const *ctx) { - const DN_U8 *data = (const DN_U8 *)bytes; - const int nblocks = len / 16; - - DN_U64 h1 = seed; - DN_U64 h2 = seed; - - const DN_U64 c1 = 0x87c37b91114253d5; - const DN_U64 c2 = 0x4cf5ad432745937f; - - //---------- - // body - - const DN_U64 *blocks = (const DN_U64 *)(data); - - for (int i = 0; i < nblocks; i++) { - DN_U64 k1 = DN_Murmur3GetBlock64_(blocks, i * 2 + 0); - DN_U64 k2 = DN_Murmur3GetBlock64_(blocks, i * 2 + 1); - - k1 *= c1; - k1 = DN_MMH3_ROTL64(k1, 31); - k1 *= c2; - h1 ^= k1; - - h1 = DN_MMH3_ROTL64(h1, 27); - h1 += h2; - h1 = h1 * 5 + 0x52dce729; - - k2 *= c2; - k2 = DN_MMH3_ROTL64(k2, 33); - k2 *= c1; - h2 ^= k2; - - h2 = DN_MMH3_ROTL64(h2, 31); - h2 += h1; - h2 = h2 * 5 + 0x38495ab5; - } - - //---------- - // tail - - const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 16); - - DN_U64 k1 = 0; - DN_U64 k2 = 0; - - switch (len & 15) { - case 15: - k2 ^= ((DN_U64)tail[14]) << 48; - case 14: - k2 ^= ((DN_U64)tail[13]) << 40; - case 13: - k2 ^= ((DN_U64)tail[12]) << 32; - case 12: - k2 ^= ((DN_U64)tail[11]) << 24; - case 11: - k2 ^= ((DN_U64)tail[10]) << 16; - case 10: - k2 ^= ((DN_U64)tail[9]) << 8; - case 9: - k2 ^= ((DN_U64)tail[8]) << 0; - k2 *= c2; - k2 = DN_MMH3_ROTL64(k2, 33); - k2 *= c1; - h2 ^= k2; - - case 8: - k1 ^= ((DN_U64)tail[7]) << 56; - case 7: - k1 ^= ((DN_U64)tail[6]) << 48; - case 6: - k1 ^= ((DN_U64)tail[5]) << 40; - case 5: - k1 ^= ((DN_U64)tail[4]) << 32; - case 4: - k1 ^= ((DN_U64)tail[3]) << 24; - case 3: - k1 ^= ((DN_U64)tail[2]) << 16; - case 2: - k1 ^= ((DN_U64)tail[1]) << 8; - case 1: - k1 ^= ((DN_U64)tail[0]) << 0; - k1 *= c1; - k1 = DN_MMH3_ROTL64(k1, 31); - k1 *= c2; - h1 ^= k1; - }; - - //---------- - // finalization - - h1 ^= len; - h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = DN_Murmur3FMix64_(h1); - h2 = DN_Murmur3FMix64_(h2); - - h1 += h2; - h2 += h1; - - DN_Murmur3 result = {}; - result.e[0] = h1; - result.e[1] = h2; + DN_U32 result = DN_Murmur3HashRawResult(ctx->it, ctx->carry, ctx->bytes_count); return result; } -DN_API DN_U64 DN_Murmur3HashU64FromBytesX64(void const *bytes, int len, DN_U32 seed) -{ - DN_Murmur3 hash = DN_Murmur3HashU128FromBytesX64(bytes, len, seed); - DN_U64 result = hash.e[0]; - return result; -} - -DN_API DN_U32 DN_Murmur3HashU32FromBytesX64(void const *bytes, int len, DN_U32 seed) -{ - DN_Murmur3 hash = DN_Murmur3HashU128FromBytesX64(bytes, len, seed); - DN_U32 result = DN_Cast(DN_U32)hash.e[0]; - return result; -} +#if defined(_MSC_VER) + #pragma warning(pop) +#endif DN_API DN_Str8x32 DN_Str8x32FromAnsiColourCodeU8Rgb(DN_AnsiColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b) { @@ -7630,36 +7993,36 @@ DN_API bool DN_V4F32Rgba01IsValid(DN_V4F32 rgba01) return result; } -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbU32(DN_U32 u32) +DN_API DN_V4F32 DN_V4F32FromRgbU32ToRgba01(DN_U32 rgb) { - DN_U8 r = (DN_U8)((u32 & 0x00FF0000) >> 16); - DN_U8 g = (DN_U8)((u32 & 0x0000FF00) >> 8); - DN_U8 b = (DN_U8)((u32 & 0x000000FF) >> 0); - DN_V4F32 result = DN_V4F32Rgba01FromRgbU8(r, g, b); + DN_U8 r = (DN_U8)((rgb & 0x00FF0000) >> 16); + DN_U8 g = (DN_U8)((rgb & 0x0000FF00) >> 8); + DN_U8 b = (DN_U8)((rgb & 0x000000FF) >> 0); + DN_V4F32 result = DN_V4F32FromRgb255ToRgba01(r, g, b); return result; } -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbaU32(DN_U32 u32) +DN_API DN_V4F32 DN_V4F32FromRgbaU32ToRgba01(DN_U32 u32) { DN_U8 r = (DN_U8)((u32 & 0xFF000000) >> 24); DN_U8 g = (DN_U8)((u32 & 0x00FF0000) >> 16); DN_U8 b = (DN_U8)((u32 & 0x0000FF00) >> 8); DN_U8 a = (DN_U8)((u32 & 0x000000FF) >> 0); - DN_V4F32 result = DN_V4F32Rgba01FromRgbaU8(r, g, b, a); + DN_V4F32 result = DN_V4F32FromRgba255ToRgba01(r, g, b, a); return result; } -#define DN_Srgb_COEFFICIENT_F32 2.2f -DN_API DN_V4F32 DN_V4F32Linear01FromSrgb01(DN_V4F32 srgb01) +#define DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32 2.2f +DN_API DN_V4F32 DN_V4F32FromSrgb01ToLinear01(DN_V4F32 srgb01) { DN_Assert(srgb01.x >= 0.f && srgb01.x <= 1.f); DN_Assert(srgb01.y >= 0.f && srgb01.y <= 1.f); DN_Assert(srgb01.z >= 0.f && srgb01.z <= 1.f); DN_Assert(srgb01.a >= 0.f && srgb01.a <= 1.f); DN_V4F32 result = {}; - result.r = DN_PowF32(srgb01.r, DN_Srgb_COEFFICIENT_F32); - result.g = DN_PowF32(srgb01.g, DN_Srgb_COEFFICIENT_F32); - result.b = DN_PowF32(srgb01.b, DN_Srgb_COEFFICIENT_F32); + result.r = DN_PowF32(srgb01.r, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.g = DN_PowF32(srgb01.g, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.b = DN_PowF32(srgb01.b, DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); result.a = srgb01.a; return result; } @@ -7672,16 +8035,16 @@ DN_API DN_V4F32 DN_V4F32Linear01Desaturate(DN_V4F32 linear01, DN_F32 t01) return result; } -DN_API DN_V4F32 DN_V4F32Srgb01FromLinear01(DN_V4F32 linear01) +DN_API DN_V4F32 DN_V4F32FromLinear01ToSrgb01(DN_V4F32 linear01) { DN_Assert(linear01.x >= 0.f && linear01.x <= 1.f); DN_Assert(linear01.y >= 0.f && linear01.y <= 1.f); DN_Assert(linear01.z >= 0.f && linear01.z <= 1.f); DN_Assert(linear01.a >= 0.f && linear01.a <= 1.f); DN_V4F32 result = {}; - result.r = DN_PowF32(linear01.r, 1.f / DN_Srgb_COEFFICIENT_F32); - result.g = DN_PowF32(linear01.g, 1.f / DN_Srgb_COEFFICIENT_F32); - result.b = DN_PowF32(linear01.b, 1.f / DN_Srgb_COEFFICIENT_F32); + result.r = DN_PowF32(linear01.r, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.g = DN_PowF32(linear01.g, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); + result.b = DN_PowF32(linear01.b, 1.f / DN_SRGB01_TO_LINEAR01_COEFFICIENT_F32); result.a = linear01.a; return result; } @@ -8075,6 +8438,13 @@ DN_API DN_M2x3 DN_M2x3Identity() return result; } +DN_API bool DN_M2x3IsIdentity(DN_M2x3 m2x3) +{ + DN_M2x3 identity = DN_M2x3Identity(); + bool result = DN_M2x3Eq(&m2x3, &identity); + return result; +} + DN_API DN_M2x3 DN_M2x3Translate(DN_V2F32 offset) { DN_M2x3 result = { @@ -8414,10 +8784,11 @@ DN_API DN_Rect DN_RectCutCut(DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip DN_Rect result = {}; if (rect_cut.rect) { switch (rect_cut.side) { - case DN_RectCutSide_Left: result = DN_RectCutLeftClip(rect_cut.rect, size.w, clip); break; - case DN_RectCutSide_Right: result = DN_RectCutRightClip(rect_cut.rect, size.w, clip); break; - case DN_RectCutSide_Top: result = DN_RectCutTopClip(rect_cut.rect, size.h, clip); break; - case DN_RectCutSide_Bottom: result = DN_RectCutBottomClip(rect_cut.rect, size.h, clip); break; + case DN_Side_Left: result = DN_RectCutLeftClip (rect_cut.rect, size.w, clip); break; + case DN_Side_Right: result = DN_RectCutRightClip (rect_cut.rect, size.w, clip); break; + case DN_Side_Top: result = DN_RectCutTopClip (rect_cut.rect, size.h, clip); break; + case DN_Side_Bottom: result = DN_RectCutBottomClip(rect_cut.rect, size.h, clip); break; + case DN_Side_Count: result = *rect_cut.rect; DN_AssertInvalidCodePath; break; } } return result; @@ -8826,16 +9197,6 @@ DN_API DN_USize DN_ArrayCopyPtrPoolAssert(void **data, void const *src, DN_USize return result; } -DN_API void *DN_SinglyLLDetach(void **link, void **next) -{ - void *result = *link; - if (*link) { - *link = *next; - *next = nullptr; - } - return result; -} - DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size) { DN_U64 avail = ring->write_pos - ring->read_pos; @@ -8883,7 +9244,7 @@ DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size) DN_API DN_U32 DN_HTableHashFuncMurmur3KeyBytes(void const *key, DN_USize size) { DN_U32 const DEFAULT_SEED = 0xb255b383; - DN_U32 result = DN_Murmur3HashU32FromBytes(key, DN_Cast(int)size, DEFAULT_SEED); + DN_U32 result = DN_Murmur3Hash(DEFAULT_SEED, key, DN_Cast(int)size); return result; } @@ -9075,10 +9436,11 @@ DN_API bool DN_HTableResize(DN_HTable *table, DN_USize new_max) void* new_kvs = {}; DN_USize new_kvs_size = new_max * table->size_of_kv; - if (table->flags & DN_HTableFlags_UsingHeap) - new_kvs = DN_HeapAlloc(&table->heap, /*reserve*/ new_kvs_size, /*commit*/ new_kvs_size); - else + if (table->flags & DN_HTableFlags_UsingHeap) { + new_kvs = DN_HeapAllocCallSite(&table->heap, /*reserve*/ new_kvs_size, /*commit*/ new_kvs_size, DN_HeapAllocFlag_Nil, DN_CallSiteNowNamed("DN HTable Resize")); + } else { new_kvs = DN_PoolAlloc(&table->pool, new_max * table->size_of_kv); + } if (new_kvs) { // NOTE: Copy over the old table slots into the new table storage @@ -10401,7 +10763,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) for (DN_TestScopeF(&result, "[Arena] Reused memory is zeroed out")) { uint8_t alignment = 1; DN_USize alloc_size = DN_Kilobytes(128); - DN_MemList mem = DN_MemListFromHeap(0, 0, DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(0, 0, DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Reused memory"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -10423,7 +10785,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) } for (DN_TestScopeF(&result, "[Arena] Grows naturally, 1mb + 4mb")) { - DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Grows naturally"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -10445,7 +10807,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) } for (DN_TestScopeF(&result, "[Arena] Grows naturally, 1mb, temp memory 4mb")) { - DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_MemList mem = DN_MemListFromHeap(DN_Megabytes(2), DN_Megabytes(2), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "Grows naturally"); DN_Arena arena = DN_ArenaFromMemList(&mem); DN_DEFER { DN_MemListDeinit(&mem); }; @@ -11619,7 +11981,7 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) for (DN_ForItSize(entry_it, NETEntry, entries, entries_count)) { NETEntry *entry = entry_it.data; - DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "NET request"); DN_Str8 remote_ws_server_url = DN_Str8Lit("wss://echo.websocket.org"); DN_Str8 remote_http_server_url = DN_Str8Lit("https://google.com"); @@ -11692,28 +12054,88 @@ DN_API DN_TestCore DN_TestSuite(DN_Arena *arena_) DN_MSVC_WARNING_POP #endif // #if DN_WITH_TESTS -DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted) +DN_API DN_MemDebuggerSnapshot DN_MemDebuggerSnapshotMake(DN_MemDebugger *debugger, DN_Arena *arena) +{ + DN_MemDebuggerSnapshot result = {}; + if (!debugger) + return result; + + DN_TicketMutexBegin(&debugger->alloc_table_mutex); + { + DN_USize count_req = debugger->alloc_table.count; + + // NOTE: Exclude this allocation from the memory debugger otherwise that will call into the + // memory debugger which engages the `alloc_table_mutex` it self causing a dead-lock. We restore + // the flag, if it was set after the fact. + DN_HeapFlags flags_copy = arena->mem->heap.flags; + { + if (arena->mem->flags & DN_MemFlags_Heap) + arena->mem->heap.flags &= ~DN_HeapFlags_ExcludeFromMemDebugger; + + // NOTE: Allocate memory + result.count = 0; + result.data = DN_ArenaNewArrayZ(arena, DN_MemDebuggerSnapshotPtrData, count_req); + } + arena->mem->heap.flags = flags_copy; + + // NOTE: Snapshot the allocation table + if (result.data) { + for (DN_ForItSize(it, DN_MemDebuggerKV, debugger->alloc_table_kvs, debugger->alloc_table.max)) { + DN_MemDebuggerKV *kv = it.data; + if (!DN_HTableHashIsValue(kv->hash)) + continue; + + DN_MemDebuggerPtrData const *alloc = &kv->value; + if (alloc->flags & DN_MemDebuggerPtrFlag_Freed) { + result.freed_count++; + result.freed_bytes += alloc->size; + } else { + DN_MemDebuggerSnapshotPtrData *data = DN_PArrayMakeZ(result.data, &result.count, count_req); + data->ptr = alloc->ptr; + data->size = alloc->size; + data->flags = alloc->flags; + // NOTE: The strings in the call site are in the data-segment so shallow copy is fine + data->call_site = alloc->call_site; + data->stack_trace = DN_Str8FromStr8Arena(alloc->stack_trace, arena); + result.active_bytes += data->size; + } + } + } + } + DN_TicketMutexEnd(&debugger->alloc_table_mutex); + return result; +} + +bool DN_MemDebuggerSnapshotPtrDataQSortComparePtrLT(void const *lhs, void const *rhs, void*) +{ + DN_MemDebuggerSnapshotPtrData const *lhs_it = DN_Cast(DN_MemDebuggerSnapshotPtrData *) lhs; + DN_MemDebuggerSnapshotPtrData const *rhs_it = DN_Cast(DN_MemDebuggerSnapshotPtrData *) rhs; + bool result = DN_Cast(DN_UPtr) lhs_it->ptr < DN_Cast(DN_UPtr) rhs_it->ptr; + return result; +} + +DN_API void DN_MemDebuggerAlloc_(DN_MemDebugger *debugger, void *ptr, DN_USize size, bool leak_permitted, DN_CallSite call_site) { if (!ptr) return; - DN_TicketMutexBegin(&leak->alloc_table_mutex); + DN_TicketMutexBegin(&debugger->alloc_table_mutex); - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_HTable* alloc_table = &leak->alloc_table; - DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; - DN_HTableAddResult alloc_entry = DN_HTableMake(alloc_table, &uptr); - DN_LeakAlloc* alloc = DN_Cast(DN_LeakAlloc *)alloc_entry.slot.value; + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_HTable* alloc_table = &debugger->alloc_table; + DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; + DN_HTableAddResult alloc_entry = DN_HTableMake(alloc_table, &uptr); + DN_MemDebuggerPtrData* alloc = DN_Cast(DN_MemDebuggerPtrData *)alloc_entry.slot.value; if (alloc_entry.existed) { - if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { + if ((alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0) { DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); DN_AssertAlwaysF( - alloc->flags & DN_LeakAllocFlag_Freed, + alloc->flags & DN_MemDebuggerPtrFlag_Freed, "This pointer is already in the leak tracker, however it has not been freed yet. This " "same pointer is being ask to be tracked twice in the allocation table, e.g. one if its " "previous free calls has not being marked freed with an equivalent call to " - "DN_LeakTrackDealloc()\n" + "DN_MemDebuggerDealloc()\n" "\n" "The pointer (0x%p) originally allocated %.*s at:\n" "\n" @@ -11730,42 +12152,46 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b } // NOTE: Pointer was reused, clean up the prior entry - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.count; - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.count; + debugger->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.count; + debugger->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.count; DN_OS_MemDealloc(alloc->stack_trace.data); DN_OS_MemDealloc(alloc->freed_stack_trace.data); *alloc = {}; } - alloc->ptr = ptr; - alloc->size = size; - alloc->stack_trace = stack_trace; - alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.count; - DN_TicketMutexEnd(&leak->alloc_table_mutex); + alloc->ptr = ptr; + alloc->size = size; + alloc->stack_trace = stack_trace; + // NOTE: Strings inside the callsite are c-string literals stored in the data segment. So no deep + // copy is needed. + // TODO: Except, if we do DLL reloading, that's going to unload the string. + alloc->call_site = call_site; + alloc->flags |= leak_permitted ? DN_MemDebuggerPtrFlag_LeakPermitted : 0; + debugger->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.count; + DN_TicketMutexEnd(&debugger->alloc_table_mutex); } -DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) +DN_API void DN_MemDebuggerDealloc_(DN_MemDebugger *debugger, void *ptr) { if (!ptr) return; - DN_TicketMutexBegin(&leak->alloc_table_mutex); + DN_TicketMutexBegin(&debugger->alloc_table_mutex); - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_HTable* alloc_table = &leak->alloc_table; - DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; - DN_LeakAlloc* alloc = DN_Cast(DN_LeakAlloc *)DN_HTableValueFromFind(alloc_table, &uptr); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_HTable* alloc_table = &debugger->alloc_table; + DN_UPtr uptr = DN_Cast(DN_UPtr)ptr; + DN_MemDebuggerPtrData* alloc = DN_Cast(DN_MemDebuggerPtrData *)DN_HTableValueFromFind(alloc_table, &uptr); DN_AssertAlwaysF(alloc, "Allocated pointer can not be removed as it does not exist in the " "allocation table. When this memory was allocated, the pointer was " "not added to the allocation table [ptr=%p]", ptr); - if (alloc->flags & DN_LeakAllocFlag_Freed) { + if (alloc->flags & DN_MemDebuggerPtrFlag_Freed) { DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); - DN_AssertAlwaysF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, + DN_AssertAlwaysF((alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0, "Double free detected, pointer to free was already marked " "as freed. Either the pointer was reallocated but not " "traced, or, the pointer was freed twice.\n" @@ -11789,24 +12215,24 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) } DN_Assert(alloc->freed_stack_trace.count == 0); - alloc->flags |= DN_LeakAllocFlag_Freed; - alloc->freed_stack_trace = stack_trace; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.count; - DN_TicketMutexEnd(&leak->alloc_table_mutex); + alloc->flags |= DN_MemDebuggerPtrFlag_Freed; + alloc->freed_stack_trace = stack_trace; + debugger->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.count; + DN_TicketMutexEnd(&debugger->alloc_table_mutex); } -DN_API void DN_LeakDump_(DN_LeakTracker *leak) +DN_API void DN_MemDebuggerDump_(DN_MemDebugger *debugger) { DN_U64 leak_count = 0; DN_U64 leaked_bytes = 0; - for (DN_ForItSize(it, DN_LeakTrackerKV, leak->alloc_table_kvs, leak->alloc_table.max)) { - DN_LeakTrackerKV *kv = it.data; + for (DN_ForItSize(it, DN_MemDebuggerKV, debugger->alloc_table_kvs, debugger->alloc_table.max)) { + DN_MemDebuggerKV *kv = it.data; if (!DN_HTableHashIsValue(kv->hash)) continue; - DN_LeakAlloc *alloc = &kv->value; - bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0; - bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted); + DN_MemDebuggerPtrData *alloc = &kv->value; + bool alloc_leaked = (alloc->flags & DN_MemDebuggerPtrFlag_Freed) == 0; + bool leak_permitted = (alloc->flags & DN_MemDebuggerPtrFlag_LeakPermitted); if (alloc_leaked && !leak_permitted) { leaked_bytes += alloc->size; leak_count++; @@ -12085,17 +12511,17 @@ DN_API DN_Heap DN_OS_HeapInitDefault() return result; } -DN_API DN_Arena DN_OS_ArenaFromHeapVirtual(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags) +DN_API DN_Arena DN_OS_ArenaFromHeapVirtualCallSite(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_CallSite call_site) { DN_Heap heap = DN_OS_HeapInitVirtual(); - DN_Arena result = DN_ArenaFromHeap(reserve, commit, flags, heap); + DN_Arena result = DN_ArenaFromHeapCallSite(reserve, commit, flags, heap, call_site); return result; } -DN_API DN_Arena DN_OS_ArenaFromHeapBasic(DN_U64 size, DN_MemFlags flags) +DN_API DN_Arena DN_OS_ArenaFromHeapBasicCallSite(DN_U64 size, DN_MemFlags flags, DN_CallSite call_site) { DN_Heap heap = DN_OS_HeapInitBasic(); - DN_Arena result = DN_ArenaFromHeap(/*reserve=*/ size, /*commit=*/ size, flags, heap); + DN_Arena result = DN_ArenaFromHeapCallSite(/*reserve=*/ size, /*commit=*/ size, flags, heap, call_site); return result; } @@ -12139,10 +12565,15 @@ DN_API DN_Str8 DN_OS_ExeDir(DN_Arena *arena) DN_API void DN_OS_OpenUrl(DN_Str8 url) { + #if defined(DN_PLATFORM_WIN32) DN_TcScratch scratch = DN_TcScratchBeginArena(nullptr, 0); DN_Str16 url16 = DN_OS_W32Str8ToStr16(&scratch.arena, url); ShellExecuteW(0, L"open", (WCHAR *)url16.data, 0, 0, SW_SHOWNORMAL); DN_TcScratchEnd(&scratch); + #else + (void)url; + DN_AssertInvalidCodePathF("Unimplemented"); + #endif } // NOTE: Counters @@ -12521,6 +12952,54 @@ DN_API void DN_OS_WindowMinimise(DN_OSWindow *window, DN_OSWindowMinimise minimi } } +DN_API void DN_OS_WindowCustomTitlebarSet(DN_OSWindow *window, bool custom_titlebar) +{ + #if defined(DN_PLATFORM_WIN32) + HWND hwnd = window ? DN_Cast(HWND) window->handle : nullptr; + DN_OSW32Core *w32 = DN_OS_W32GetCore(); + DN_OSW32Window *w32_window = DN_OS_W32WindowFromHwndMaybeAlloc(w32, hwnd, DN_TcMainArena()); + if (w32_window->custom_title_bar != custom_titlebar) { + w32_window->custom_title_bar = custom_titlebar; + + // NOTE: We force a NCCALCSIZE message to be sent here which triggers the custom border logic + // For this to work the windows procedure must be handling NCCALCSIZE and NCHITTEST otherwise this + // is going to do nothing. + // + // For example in sokol you can do something like this to equip a window proc that handles + // custom titlebars. + // + // HWND hwnd = DN_Cast(HWND) sapp_win32_get_hwnd(); + // DN_OSW32Core *w32 = DN_OS_W32GetCore(); + // w32->fallback_wnd_proc = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC); + // SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)DN_OS_W32WindowProcCustomTitlebar); + // + // In your code, you should set w32_window->custom_title_bar_rect_that_is_draggable to the + // region of the title bar that can be dragged to move the window around (excluding any buttons + // or controls that shouldn't respond to this interaction). + SetWindowPos(w32_window->hwnd, NULL, 0, 0, 0, 0, + SWP_FRAMECHANGED | // Recalculate frame (triggers WM_NCCALCSIZE) + SWP_NOMOVE | // Don't move window + SWP_NOSIZE | // Don't resize window + SWP_NOZORDER | // Don't change Z order + SWP_NOACTIVATE | // Don't activate window + SWP_NOOWNERZORDER); // Don't change owner Z order + + if (w32_window->custom_title_bar) { + // NOTE: Call DwmExtendFrameIntoClientArea with the margins (-1,-1,-1,-1), which, magically, + // does, something, related to how windows treats the borders. LLM says it enables DWM + // shadow/composition. + // + // From Freya Holmer + // https://discord.com/channels/239737791225790464/1294659776449609788/1294661218513977445 + MARGINS margins = {-1, -1, -1, -1}; + DwmExtendFrameIntoClientArea(w32_window->hwnd, &margins); + } + } + #else + DN_AssertInvalidCodePath; + #endif +} + static void DN_OS_ThreadExecute_(void *user_context) { DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; @@ -13207,7 +13686,9 @@ DN_API DN_Str8 DN_Str8FromStackTraceNowArena(DN_Arena *arena, DN_U16 limit, DN_U DN_API DN_Str8 DN_Str8FromStackTraceNowHeap(DN_U16 limit, DN_U16 skip) { - DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_Kilobytes(64), DN_MemFlags_NoAllocTrack, DN_OS_HeapInitBasic()); + DN_Heap heap = DN_OS_HeapInitBasic(); + heap.flags |= DN_HeapFlags_ExcludeFromMemDebugger; + DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_Kilobytes(64), DN_MemFlags_Nil, heap, "DN Stack Trace Arena"); DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); DN_StackTrace walk = DN_StackTraceFromArena(&arena, limit); DN_StackTraceAddToStr8Builder_(&walk, &builder, skip); @@ -13411,7 +13892,7 @@ DN_NETRequestHandle DN_NET_SetupRequest(DN_NETRequest *request, DN_Str8 url, DN_ "free-list and reusing it. This is so that we centralise the one place that we " "reinitialise the temp arena for the request into this codepath."); else - request->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(1), DN_MemFlags_Nil, DN_OS_HeapInitVirtual()); + request->mem = DN_MemListFromHeap(DN_Megabytes(1), DN_Kilobytes(1), DN_MemFlags_Nil, DN_OS_HeapInitVirtual(), "DN NET Request MemList"); request->arena = DN_ArenaTempBeginFromMemList(&request->mem); request->type = type; diff --git a/Source/dn.h b/Source/dn.h index 3bda49f..0259639 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -182,11 +182,17 @@ // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. +// Memory Debugger +// Set `DN_MEM_DEBUGGER` to `1` to enable memory debugging which enables the `DN_Core`'s +// `mem_debugger` field, tracks all allocations in the application and reports leaks. +// +// #define DN_MEM_DEBUGGER 1 + // Paranoia Level // Set the `DN_PARANOIA_LEVEL` to an integer value to enable various validation layers and // error checking mechanisms in the codebase and primitives exposed by the library -// (default: PARANOID level 1 in debug builds, 0 otherwise) -// paranoia level 0 in release builds and level 1 for debug. +// (default: PARANOIA level 1 in debug builds, 0 otherwise) paranoia level 0 in release builds +// and level 1 for debug. // // #define DN_PARANOIA_LEVEL 1 // @@ -558,11 +564,11 @@ #define DN_ForIndexU(index, count) DN_USize index = 0; index < count; index++ #define DN_ForIndexI(index, count) DN_ISize index = 0; index < count; index++ -#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index -#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) - 1, &(array)[count - 1]}; it.index < (count); it.index--, it.data = (array) + it.index -#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index -#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) -#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index +#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index +#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) ? (count) - 1 : 0, (count) ? &(array)[count - 1] : 0}; it.index < (count); it.index--, it.data = (array) + it.index +#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index +#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) +#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index #define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1)) #define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1)) @@ -786,6 +792,14 @@ typedef enum DN_ZMem { DN_ZMem_Yes, // Memory should be zero-ed out before giving to the callee } DN_ZMem; +typedef enum DN_Side { + DN_Side_Left, + DN_Side_Right, + DN_Side_Top, + DN_Side_Bottom, + DN_Side_Count, +} DN_Side; + typedef enum DN_AddType { DN_AddType_Prepend, DN_AddType_Append, @@ -1000,12 +1014,14 @@ DN_MSVC_WARNING_POP typedef struct DN_CallSite DN_CallSite; struct DN_CallSite { + DN_Str8 name; DN_Str8 file; DN_Str8 function; DN_U32 line; }; -#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(""), DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNowNamed(name_cstr) DN_Literal(DN_CallSite){DN_Str8Lit(name_cstr), DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } #if defined(__cplusplus) template @@ -1131,9 +1147,9 @@ enum DN_MemPage_ // This flag must only be used in DN_Mem_Protect DN_MemPage_Guard = 1 << 3, - // If leak tracing is enabled, this flag will allow the allocation recorded - // from the reserve call to be leaked, e.g. not printed when leaks are - // dumped to the console. + // If memory debugging is enabled, this flag will allow the allocation + // recorded from the reserve call to be leaked, e.g. not printed when leaks + // are dumped to the console. DN_MemPage_AllocRecordLeakPermitted = 1 << 4, // If leak tracing is enabled this flag will prevent any allocation record @@ -1165,15 +1181,31 @@ typedef enum DN_HeapType { DN_HeapType_Virtual, } DN_HeapType; +typedef DN_U32 DN_HeapAllocFlag; +enum DN_HeapAllocFlag_ { + DN_HeapAllocFlag_Nil = 0, + DN_HeapAllocFlag_MemDebuggerCanLeakPtr = 1 << 0, + DN_HeapAllocFlag_MemDebuggerExcludePtr = 1 << 1, +}; + +typedef DN_U32 DN_HeapFlags; +enum DN_HeapFlags_ { + DN_HeapFlags_Nil = 0, + DN_HeapFlags_ExcludeFromMemDebugger = 1 << 0, + DN_HeapFlags_MemDebuggerCanLeakPtr = 1 << 1, +}; + typedef void *(DN_HeapBasicAllocFunc) (DN_USize count); typedef void (DN_HeapBasicDeallocFunc)(void *ptr); typedef void *(DN_HeapVirtualReserveFunc)(DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); typedef bool (DN_HeapVirtualCommitFunc)(void *ptr, DN_USize count, DN_U32 page_flags); typedef void (DN_HeapVirtualReleaseFunc)(void *ptr, DN_USize count); + typedef struct DN_Heap DN_Heap; struct DN_Heap { + DN_HeapFlags flags; DN_HeapType type; DN_USize bytes_alloc_total; DN_USize bytes_alloc; @@ -1220,8 +1252,6 @@ enum DN_MemFlags_ DN_MemFlags_Nil = 0, DN_MemFlags_NoGrow = 1 << 0, DN_MemFlags_NoPoison = 1 << 1, - DN_MemFlags_NoAllocTrack = 1 << 2, - DN_MemFlags_AllocCanLeak = 1 << 3, DN_MemFlags_SimAlloc = 1 << 4, // NOTE: Records stack traces of temp memory regions on construction to provide more diagnostics @@ -1251,12 +1281,16 @@ enum DN_MemFlags_ typedef struct DN_MemList DN_MemList; struct DN_MemList { - DN_MemBlock* curr; + DN_MemBlock* curr; // Contiguous blocks of memory, chained for growth, parcelled for object allocation DN_MemFlags flags; - DN_Heap heap; + DN_Heap heap; // Backing allocator for the memory blocks DN_MemStats stats; DN_Str8 label; + #if DN_MEM_DEBUGGER + DN_USize mem_debugger_id; + #endif + #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_U32 uaf_guard_next_id; DN_U32 uaf_guard_active_id; @@ -1387,7 +1421,8 @@ struct DN_Utf32FromIterator bool init; bool success; DN_Str8 remaining; - DN_USize bytes_decoded; + DN_USize bytes_decoded_; + DN_USize bytes_decoded_total; DN_USize codepoint_index; DN_U32 codepoint; }; @@ -1635,6 +1670,97 @@ struct DN_Profiler DN_F64 frame_avg_tsc; }; +struct DN_Profiler2Marker +{ + DN_Str8 name; + DN_U64 tsc_begin; + DN_U64 tsc_end; + DN_U64 tsc_children_duration; + DN_CallSite call_site; + DN_U32 parent_index; + DN_U32 next_index; + DN_U32 first_child_index; + DN_U32 last_child_index; +}; + +struct DN_Profiler2Frame +{ + // NOTE: The frame number that this frame was part of. Frame number is incremented each time + // frame-begin is called. + DN_U32 frame_num; + + // NOTE: The markers array is not contiguous. We using a ring-buffer so accessing the marker must + // be done like `profiler->markers + ((frame->begin_offset + i) % profiler->markers_max)`. A + // helper function is available `DN_Profiler2MarkerFromFrameIndex(profiler, frame, i)` + DN_U32 begin_offset; + DN_U32 count; + DN_U32 max; + + // NOTE: The profiling marker that the profiler makes and closes when frame-begin is called. + // Encapsulates essentially the duration of 1 update loop in an interactive application. + DN_Profiler2Marker* begin_marker; +}; + +struct DN_Profiler2Zone +{ + struct DN_Profiler2* profiler; + DN_Profiler2Marker* marker; + bool ended; + bool scope_once; +}; + +enum DN_Profiler2State +{ + DN_Profiler2State_Run, + DN_Profiler2State_Paused, + DN_Profiler2State_UnpauseEnqueued, +}; + +struct DN_Profiler2 +{ + DN_Profiler2State state; + DN_U32 frame_num; + DN_ProfilerTscNowFunc* tsc_now; + DN_U64 tsc_frequency; + + DN_Profiler2Frame* frames; + DN_U32 frames_max; + DN_U32 frames_write_index_unwrapped; + + DN_Profiler2Marker* markers; + DN_U32 markers_max; + + DN_Profiler2Frame* active_frame; + DN_U32 active_parent_index; + + struct DN_Profiler2Zone frame_begin_zone; +}; + +struct DN_Profiler2Iterator +{ + bool init; + DN_USize iteration_count; + DN_U32 marker_index; + DN_U32 last_visited_index; + DN_Profiler2Marker* marker; +}; + +struct DN_Profiler2FunctionTime +{ + DN_CallSite call_site; + DN_Str8 name; + DN_USize hit_count; + DN_U64 tsc_total; + DN_U64 tsc_children_total; +}; + +struct DN_Profiler2FunctionTimes +{ + DN_Profiler2FunctionTime *data; + DN_USize count; + DN_USize max; +}; + typedef bool (DN_QSortCompareFunc) (void const *lhs, void const *rhs, void *user_context); typedef bool (DN_BSearchLessThanFunc)(void const *lhs, void const *rhs, void *user_context); enum DN_BSearchType @@ -1761,8 +1887,13 @@ typedef enum DN_TcDeinitArenas { typedef struct DN_Pcg32 DN_Pcg32; struct DN_Pcg32 { DN_U64 state; }; -typedef struct DN_Murmur3 DN_Murmur3; -struct DN_Murmur3 { DN_U64 e[2]; }; +typedef struct DN_Murmur3Context DN_Murmur3Context; +struct DN_Murmur3Context +{ + DN_U32 it; + DN_U32 carry; + DN_U32 bytes_count; +}; typedef enum DN_LogType { DN_LogType_Debug, @@ -1941,18 +2072,11 @@ typedef enum DN_RectCutClip { DN_RectCutClip_Yes, } DN_RectCutClip; -typedef enum DN_RectCutSide { - DN_RectCutSide_Left, - DN_RectCutSide_Right, - DN_RectCutSide_Top, - DN_RectCutSide_Bottom, -} DN_RectCutSide; - typedef struct DN_RectCut DN_RectCut; struct DN_RectCut { - DN_Rect* rect; - DN_RectCutSide side; + DN_Rect* rect; + DN_Side side; }; typedef struct DN_RaycastV2 DN_RaycastV2; @@ -2110,39 +2234,56 @@ enum DN_HTableAllowTombstone DN_HTableAllowTombstone_Yes, }; -enum DN_LeakAllocFlag +enum DN_MemDebuggerPtrFlag { - DN_LeakAllocFlag_Freed = 1 << 0, - DN_LeakAllocFlag_LeakPermitted = 1 << 1, + DN_MemDebuggerPtrFlag_Freed = 1 << 0, + DN_MemDebuggerPtrFlag_LeakPermitted = 1 << 1, }; -typedef struct DN_LeakAlloc DN_LeakAlloc; -struct DN_LeakAlloc +typedef struct DN_MemDebuggerPtrData DN_MemDebuggerPtrData; +struct DN_MemDebuggerPtrData { - void *ptr; // 8 Pointer to the allocation being tracked - DN_USize size; // 16 Size of the allocation - DN_USize freed_size; // 24 Store the size of the allocation when it is freed - DN_Str8 stack_trace; // 40 Stack trace at the point of allocation - DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed - DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` + DN_CallSite call_site; + DN_U16 flags; // Bit flags from `DN_MemDebuggerPtrFlag` + void* ptr; // Pointer to the allocation being tracked + DN_USize size; // Size of the allocation + DN_USize freed_size; // Store the size of the allocation when it is freed + DN_Str8 stack_trace; // Stack trace at the point of allocation + DN_Str8 freed_stack_trace; // Stack trace of where the allocation was freed }; -// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get -// expensive. Enforce that there is no unexpected padding. -DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively - -typedef struct DN_LeakTrackerKV DN_LeakTrackerKV; -struct DN_LeakTrackerKV +typedef struct DN_MemDebuggerKV DN_MemDebuggerKV; +struct DN_MemDebuggerKV { - DN_HTableHashType hash; - DN_UPtr key; - DN_LeakAlloc value; + DN_HTableHashType hash; + DN_UPtr key; + DN_MemDebuggerPtrData value; }; -typedef struct DN_LeakTracker DN_LeakTracker; -struct DN_LeakTracker +typedef struct DN_MemDebuggerSnapshotPtrData DN_MemDebuggerSnapshotPtrData; +struct DN_MemDebuggerSnapshotPtrData { - DN_LeakTrackerKV *alloc_table_kvs; + void* ptr; + DN_USize size; + DN_U16 flags; + DN_CallSite call_site; + DN_Str8 stack_trace; +}; + +typedef struct DN_MemDebuggerSnapshot DN_MemDebuggerSnapshot; +struct DN_MemDebuggerSnapshot +{ + DN_MemDebuggerSnapshotPtrData *data; + DN_USize count; + DN_U64 active_bytes; + DN_U64 freed_count; + DN_U64 freed_bytes; +}; + +typedef struct DN_MemDebugger DN_MemDebugger; +struct DN_MemDebugger +{ + DN_MemDebuggerKV *alloc_table_kvs; DN_HTable alloc_table; DN_TicketMutex alloc_table_mutex; DN_U64 alloc_table_bytes_allocated_for_stack_traces; @@ -2156,7 +2297,6 @@ enum DN_InitFlags_ // NOTE: Query the OS for information and enable functionality that requires the OS (like virtual // memory APIs, mutexes, secure RNG) as well as per-thread persistent and scratch allocators. DN_InitFlags_OS = (1 << 0), - DN_InitFlags_LeakTracker = (1 << 1) | DN_InitFlags_OS, DN_InitFlags_LogLibFeatures = (1 << 2), DN_InitFlags_LogCPUFeatures = (1 << 3) | DN_InitFlags_OS, DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures, @@ -2653,7 +2793,8 @@ struct DN_Core DN_InitFlags init_flags; DN_TcCore main_tc; DN_USize mem_allocs_frame; - DN_LeakTracker leak; + DN_MemDebugger mem_debugger; + DN_USize mem_debugger_mem_list_id_counter; DN_LogType log_level_to_show_from; DN_LogPrintFunc* print_func; @@ -2850,14 +2991,17 @@ DN_API DN_MemStats DN_MemStatsSumArray // API // DN_HeapAlloc -// DN_HeapDealloc -// Allocate and deallocate memory. Note that `DN_HeapAlloc` takes reserve and commit even for -// the basic allocator. In the basic allocator which does not have a concept of reserving and -// committing, the `commit` value is ignored and the `reserve` amount is the amount that will be -// allocated. +// Allocate memory using the given heap's configured allocation functions. For a heap using the +// basic allocation routines, `commit` is ignored, `reserve` is the amount of bytes to be +// allocated and implicitly committed. For virtual memory then `reserve` is the amount of +// address space to reserve for the application from the OS. `commit` is the amount of memory in +// pages that the OS will give your application. + +#define DN_MemMetadataInit(name_cstr) DN_Literal(DN_MemMetadata){DN_CallSiteNow, DN_Str8Lit(name_cstr)} DN_API DN_Heap DN_HeapInitBasic (DN_HeapBasicAllocFunc *basic_alloc, DN_HeapBasicDeallocFunc *basic_dealloc); DN_API DN_Heap DN_HeapInitVirtual (DN_U32 page_size, DN_HeapVirtualReserveFunc *virtual_reserve, DN_HeapVirtualCommitFunc *virtual_commit, DN_HeapVirtualReleaseFunc *virtual_release); -DN_API void* DN_HeapAlloc (DN_Heap *heap, DN_USize reserve, DN_USize commit); +DN_API void* DN_HeapAllocCallSite (DN_Heap *heap, DN_USize reserve, DN_USize commit, DN_HeapAllocFlag flags, DN_CallSite call_site); +#define DN_HeapAlloc(heap, reserve, commit, flags, name_cstr) DN_HeapAllocMetadata(heap, reserve, commit, flags, DN_MemMetadataInit(name_cstr)); DN_API void DN_HeapDealloc (DN_Heap *heap, void *ptr, DN_USize size); // NOTE: MemList @@ -2869,32 +3013,33 @@ DN_API void DN_HeapDealloc // // In general for 99% of use-cases you want an `Arena` and if some API is not exposed on the // `Arena` to then use the `MemList` API on the embedded `mem_list` backing the Arena. -DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); -DN_API DN_MemList DN_MemListFromHeap (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap); -DN_API void DN_MemListDeinit (DN_MemList *mem); -DN_API bool DN_MemListCommit (DN_MemList *mem, DN_U64 size); -DN_API bool DN_MemListCommitTo (DN_MemList *mem, DN_U64 pos); -DN_API bool DN_MemListGrow (DN_MemList *mem, DN_U64 reserve, DN_U64 commit); -DN_API void * DN_MemListAlloc (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); -DN_API void * DN_MemListAllocContiguous (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); -DN_API void * DN_MemListCopy (DN_MemList *mem, void const *data, DN_U64 size, uint8_t align); -DN_API void DN_MemListPopTo (DN_MemList *mem, DN_U64 init_used); -DN_API void DN_MemListPop (DN_MemList *mem, DN_U64 amount); -DN_API DN_U64 DN_MemListPos (DN_MemList const *mem); -DN_API void DN_MemListClear (DN_MemList *mem); -DN_API bool DN_MemListOwnsPtr (DN_MemList const *mem, void const *ptr); -DN_API DN_Str8x64 DN_MemListInfoStr8x64 (DN_MemListInfo info); -DN_API DN_MemListTemp DN_MemListTempBegin (DN_MemList *mem); -DN_API void DN_MemListTempEnd (DN_MemListTemp mem); -#define DN_MemListNew(arena, T, zmem) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), zmem) -#define DN_MemListNewZ(arena, T) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewContiguous(arena, T, zmem) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), zmem) -#define DN_MemListNewContiguousZ(arena, T) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewArray(arena, T, count, zmem) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), zmem) -#define DN_MemListNewArrayZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) -#define DN_MemListNewArrayNoZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) -#define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) -#define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) +DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); +DN_API DN_MemList DN_MemListFromHeapCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site); +#define DN_MemListFromHeap(reserve, commit, flags, heap, name_cstr) DN_MemListFromHeapCallSite(reserve, commit, flags, heap, DN_CallSiteNowNamed(name_cstr)) +DN_API void DN_MemListDeinit (DN_MemList *mem); +DN_API bool DN_MemListCommit (DN_MemList *mem, DN_U64 size); +DN_API bool DN_MemListCommitTo (DN_MemList *mem, DN_U64 pos); +DN_API bool DN_MemListGrow (DN_MemList *mem, DN_U64 reserve, DN_U64 commit); +DN_API void * DN_MemListAlloc (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); +DN_API void * DN_MemListAllocContiguous (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem); +DN_API void * DN_MemListCopy (DN_MemList *mem, void const *data, DN_U64 size, uint8_t align); +DN_API void DN_MemListPopTo (DN_MemList *mem, DN_U64 init_used); +DN_API void DN_MemListPop (DN_MemList *mem, DN_U64 amount); +DN_API DN_U64 DN_MemListPos (DN_MemList const *mem); +DN_API void DN_MemListClear (DN_MemList *mem); +DN_API bool DN_MemListOwnsPtr (DN_MemList const *mem, void const *ptr); +DN_API DN_Str8x64 DN_MemListInfoStr8x64 (DN_MemListInfo info); +DN_API DN_MemListTemp DN_MemListTempBegin (DN_MemList *mem); +DN_API void DN_MemListTempEnd (DN_MemListTemp mem); +#define DN_MemListNew(arena, T, zmem) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), zmem) +#define DN_MemListNewZ(arena, T) (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewContiguous(arena, T, zmem) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), zmem) +#define DN_MemListNewContiguousZ(arena, T) (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewArray(arena, T, count, zmem) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), zmem) +#define DN_MemListNewArrayZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_MemListNewArrayNoZ(arena, T, count) (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) +#define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) +#define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) // NOTE: Arena // Overview @@ -2940,364 +3085,371 @@ DN_API void DN_MemListTempEnd DN_ArenaTempEnd(&temp); DN_ArenaDeinit(&arena); // Frees the memory */ -DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); -DN_API DN_Arena DN_ArenaFromHeap (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap); -DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); -DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); -DN_API void DN_ArenaTempEnd (DN_Arena *arena, DN_ArenaReset reset); -DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem); -DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_arena); -DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align); -DN_API void DN_ArenaDeinit (DN_Arena *arena); -DN_API bool DN_ArenaOwnsPtr (DN_Arena const *arena, void *ptr); +DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); +DN_API DN_Arena DN_ArenaFromHeapCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap, DN_CallSite call_site); +#define DN_ArenaFromHeap(reserve, commit, flags, heap, name_cstr) DN_ArenaFromHeapCallSite(reserve, commit, flags, heap, DN_CallSiteNowNamed(name_cstr)) +DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); +DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); +DN_API void DN_ArenaTempEnd (DN_Arena *arena, DN_ArenaReset reset); +DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem); +DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_arena); +DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align); +DN_API void DN_ArenaDeinit (DN_Arena *arena); +DN_API bool DN_ArenaOwnsPtr (DN_Arena const *arena, void *ptr); -#define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) -#define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewContiguous(arena, T, zmem) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), zmem) -#define DN_ArenaNewContiguousZ(arena, T) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewArray(arena, T, count, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), zmem) -#define DN_ArenaNewArrayZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) -#define DN_ArenaNewArrayNoZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) -#define DN_ArenaNewCopy(arena, T, src) (T *)DN_ArenaCopy(arena, (src), sizeof(T), alignof(T)) -#define DN_ArenaNewArrayCopy(arena, T, src, count) (T *)DN_ArenaCopy(arena, (src), sizeof(T) * (count), alignof(T)) +#define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) +#define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewContiguous(arena, T, zmem) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), zmem) +#define DN_ArenaNewContiguousZ(arena, T) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewArray(arena, T, count, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), zmem) +#define DN_ArenaNewArrayZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_ArenaNewArrayNoZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No) +#define DN_ArenaNewCopy(arena, T, src) (T *)DN_ArenaCopy(arena, (src), sizeof(T), alignof(T)) +#define DN_ArenaNewArrayCopy(arena, T, src, count) (T *)DN_ArenaCopy(arena, (src), sizeof(T) * (count), alignof(T)) -DN_API DN_Pool DN_PoolFromArena (DN_Arena *arena, DN_U8 align); -DN_API bool DN_PoolIsValid (DN_Pool const *pool); -DN_API void * DN_PoolAlloc (DN_Pool *pool, DN_USize size); -DN_API void DN_PoolDealloc (DN_Pool *pool, void *ptr); -DN_API void * DN_PoolCopy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align); -#define DN_PoolNew(pool, T) (T *)DN_PoolAlloc(pool, sizeof(T)) -#define DN_PoolNewArray(pool, T, count) (T *)DN_PoolAlloc(pool, count * sizeof(T)) -#define DN_PoolNewCopy(pool, T, src) (T *)DN_PoolCopy (pool, (src), sizeof(T), alignof(T)) -#define DN_PoolNewArrayCopy(pool, T, src, count) (T *)DN_PoolCopy (pool, (src), sizeof(T) * (count), alignof(T)) +DN_API DN_Pool DN_PoolFromArena (DN_Arena *arena, DN_U8 align); +DN_API bool DN_PoolIsValid (DN_Pool const *pool); +DN_API void * DN_PoolAlloc (DN_Pool *pool, DN_USize size); +DN_API void DN_PoolDealloc (DN_Pool *pool, void *ptr); +DN_API void * DN_PoolCopy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align); +#define DN_PoolNew(pool, T) (T *)DN_PoolAlloc(pool, sizeof(T)) +#define DN_PoolNewArray(pool, T, count) (T *)DN_PoolAlloc(pool, count * sizeof(T)) +#define DN_PoolNewCopy(pool, T, src) (T *)DN_PoolCopy (pool, (src), sizeof(T), alignof(T)) +#define DN_PoolNewArrayCopy(pool, T, src, count) (T *)DN_PoolCopy (pool, (src), sizeof(T) * (count), alignof(T)) -DN_API DN_ErrSink* DN_ErrSinkBegin_ (DN_ErrSink *err, DN_ErrSinkMode mode, DN_CallSite call_site); -#define DN_ErrSinkBegin(err, mode) DN_ErrSinkBegin_(err, mode, DN_CallSiteNow) -#define DN_ErrSinkBeginDefault(err) DN_ErrSinkBegin(err, DN_ErrSinkMode_Nil) -DN_API bool DN_ErrSinkHasError (DN_ErrSink *err); -DN_API DN_ErrSinkMsg* DN_ErrSinkEnd (DN_Arena *arena, DN_ErrSink *err); -DN_API DN_Str8 DN_ErrSinkEndStr8 (DN_Arena *arena, DN_ErrSink *err); -DN_API void DN_ErrSinkEndIgnore (DN_ErrSink *err); -DN_API bool DN_ErrSinkEndLogError_ (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg); -#define DN_ErrSinkEndLogError(err, err_msg) DN_ErrSinkEndLogError_(err, DN_CallSiteNow, err_msg) -DN_API bool DN_ErrSinkEndLogErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkEndLogErrorFV(err, fmt, args) DN_ErrSinkEndLogErrorFV_(err, DN_CallSiteNow, fmt, args) -DN_API bool DN_ErrSinkEndLogErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkEndLogErrorF(err, fmt, ...) DN_ErrSinkEndLogErrorF_(err, DN_CallSiteNow, fmt, ##__VA_ARGS__) -DN_API void DN_ErrSinkEndExitIfErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkEndExitIfErrorF(err, exit_val, fmt, ...) DN_ErrSinkEndExitIfErrorF_(err, DN_CallSiteNow, exit_val, fmt, ##__VA_ARGS__) -DN_API void DN_ErrSinkEndExitIfErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkEndExitIfErrorFV(err, exit_val, fmt, args) DN_ErrSinkEndExitIfErrorFV_(err, DN_CallSiteNow, exit_val, fmt, args) -DN_API void DN_ErrSinkAppendFV_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_ErrSinkAppendFV(error, error_code, fmt, args) DN_ErrSinkAppendFV_(error, error_code, DN_CallSiteNow, fmt, args) -DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); -#define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CallSiteNow, fmt, ##__VA_ARGS__) +DN_API DN_ErrSink* DN_ErrSinkBegin_ (DN_ErrSink *err, DN_ErrSinkMode mode, DN_CallSite call_site); +#define DN_ErrSinkBegin(err, mode) DN_ErrSinkBegin_(err, mode, DN_CallSiteNow) +#define DN_ErrSinkBeginDefault(err) DN_ErrSinkBegin(err, DN_ErrSinkMode_Nil) +DN_API bool DN_ErrSinkHasError (DN_ErrSink *err); +DN_API DN_ErrSinkMsg* DN_ErrSinkEnd (DN_Arena *arena, DN_ErrSink *err); +DN_API DN_Str8 DN_ErrSinkEndStr8 (DN_Arena *arena, DN_ErrSink *err); +DN_API void DN_ErrSinkEndIgnore (DN_ErrSink *err); +DN_API bool DN_ErrSinkEndLogError_ (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg); +#define DN_ErrSinkEndLogError(err, err_msg) DN_ErrSinkEndLogError_(err, DN_CallSiteNow, err_msg) +DN_API bool DN_ErrSinkEndLogErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkEndLogErrorFV(err, fmt, args) DN_ErrSinkEndLogErrorFV_(err, DN_CallSiteNow, fmt, args) +DN_API bool DN_ErrSinkEndLogErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkEndLogErrorF(err, fmt, ...) DN_ErrSinkEndLogErrorF_(err, DN_CallSiteNow, fmt, ##__VA_ARGS__) +DN_API void DN_ErrSinkEndExitIfErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkEndExitIfErrorF(err, exit_val, fmt, ...) DN_ErrSinkEndExitIfErrorF_(err, DN_CallSiteNow, exit_val, fmt, ##__VA_ARGS__) +DN_API void DN_ErrSinkEndExitIfErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkEndExitIfErrorFV(err, exit_val, fmt, args) DN_ErrSinkEndExitIfErrorFV_(err, DN_CallSiteNow, exit_val, fmt, args) +DN_API void DN_ErrSinkAppendFV_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_ErrSinkAppendFV(error, error_code, fmt, args) DN_ErrSinkAppendFV_(error, error_code, DN_CallSiteNow, fmt, args) +DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); +#define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CallSiteNow, fmt, ##__VA_ARGS__) -DN_API DN_TcInitArgs DN_TcInitArgsDefault (); -DN_API void DN_TcInit (DN_TcCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); -DN_API void DN_TcInitFromHeap (DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap); -DN_API void DN_TcDeinit (DN_TcCore *tc, DN_TcDeinitArenas deinit_arenas); -DN_API void DN_TcEquip (DN_TcCore *tc); -DN_API DN_TcCore* DN_TcGet (); -DN_API DN_Arena* DN_TcMainArena (); -DN_API DN_Pool* DN_TcMainPool (); -DN_API DN_Arena DN_TcTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); -DN_API DN_Arena DN_TcTempArenaFromArena (DN_Arena **conflicts, DN_USize count); -DN_API DN_TcScratch DN_TcScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); -DN_API DN_TcScratch DN_TcScratchBeginArena (DN_Arena **conflicts, DN_USize count); -DN_API void DN_TcScratchEnd (DN_TcScratch *scratch); -DN_API void DN_TcSetFrameArena (DN_Arena *arena); -DN_API DN_Arena* DN_TcFrameArena (); -DN_API DN_ErrSink* DN_TcErrSink (); -#define DN_TcErrSinkBegin(mode) DN_ErrSinkBegin(DN_TcErrSink(), mode) -#define DN_TcErrSinkBeginDefault() DN_ErrSinkBeginDefault(DN_TcErrSink()) +DN_API DN_TcInitArgs DN_TcInitArgsDefault (); +DN_API void DN_TcInit (DN_TcCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); +DN_API void DN_TcInitFromHeap (DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap); +DN_API void DN_TcDeinit (DN_TcCore *tc, DN_TcDeinitArenas deinit_arenas); +DN_API void DN_TcEquip (DN_TcCore *tc); +DN_API DN_TcCore* DN_TcGet (); +DN_API DN_Arena* DN_TcMainArena (); +DN_API DN_Pool* DN_TcMainPool (); +DN_API DN_Arena DN_TcTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_Arena DN_TcTempArenaFromArena (DN_Arena **conflicts, DN_USize count); +DN_API DN_TcScratch DN_TcScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_TcScratch DN_TcScratchBeginArena (DN_Arena **conflicts, DN_USize count); +DN_API void DN_TcScratchEnd (DN_TcScratch *scratch); +DN_API void DN_TcSetFrameArena (DN_Arena *arena); +DN_API DN_Arena* DN_TcFrameArena (); +DN_API DN_ErrSink* DN_TcErrSink (); +#define DN_TcErrSinkBegin(mode) DN_ErrSinkBegin(DN_TcErrSink(), mode) +#define DN_TcErrSinkBeginDefault() DN_ErrSinkBeginDefault(DN_TcErrSink()) -DN_API bool DN_CharIsAlphabet (char ch); -DN_API bool DN_CharIsDigit (char ch); -DN_API bool DN_CharIsAlphaNum (char ch); -DN_API bool DN_CharIsWhitespace (char ch); -DN_API bool DN_CharIsHex (char ch); -DN_API char DN_CharToLower (char ch); -DN_API char DN_CharToUpper (char ch); +DN_API bool DN_CharIsAlphabet (char ch); +DN_API bool DN_CharIsDigit (char ch); +DN_API bool DN_CharIsAlphaNum (char ch); +DN_API bool DN_CharIsWhitespace (char ch); +DN_API bool DN_CharIsHex (char ch); +DN_API char DN_CharToLower (char ch); +DN_API char DN_CharToUpper (char ch); -DN_API DN_U64FromResult DN_U64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_U64FromResult DN_U64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_U64FromResult DN_U64FromStr8 (DN_Str8 string); -DN_API DN_U64FromResult DN_U64FromPtrDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); -DN_API DN_U64FromResult DN_U64FromPtr (void const *data, DN_USize size); -DN_API DN_U64 DN_U64FromPtrUnsafeDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); -DN_API DN_U64 DN_U64FromPtrUnsafe (void const *data, DN_USize size); -DN_API DN_U64FromResult DN_U64FromHexPtr (void const *hex, DN_USize hex_count); -DN_API DN_U64 DN_U64FromHexPtrUnsafe (void const *hex, DN_USize hex_count); -DN_API DN_U64FromResult DN_U64FromHexStr8 (DN_Str8 hex); -DN_API DN_U64 DN_U64FromHexStr8Unsafe (DN_Str8 hex); -DN_API DN_U64 DN_U64FromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get U64 stored in big-endian at the high bytes [24:32) -DN_API DN_U64FromResult DN_U64FromU8x32HiBE (DN_U8x32 const *val); // Checks [0:24) bytes aren't set before getting the U64 -DN_API DN_USize DN_USizeFromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get USize stored in big-endian at the high bytes [32 - sizeof USize:32) -DN_API DN_USizeFromResult DN_USizeFromU8x32HiBE (DN_U8x32 const *val); // Checks [0:sizeof USize) bytes aren't set before getting the U64 -DN_API DN_U32FromResult DN_U32FromHexStr8 (DN_Str8 hex); -DN_API DN_U32FromResult DN_U32FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_U32FromResult DN_U32FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_U32FromResult DN_U32FromPtr (void const *data, DN_USize size); -DN_API DN_I64FromResult DN_I64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); -DN_API DN_I64FromResult DN_I64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); -DN_API DN_I64FromResult DN_I64FromStr8 (DN_Str8 string); -DN_API DN_I64FromResult DN_I64FromPtr (void const *data, DN_USize size); -DN_API DN_I64 DN_I64FromPtrUnsafe (void const *data, DN_USize size); +DN_API DN_U64FromResult DN_U64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_U64FromResult DN_U64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_U64FromResult DN_U64FromStr8 (DN_Str8 string); +DN_API DN_U64FromResult DN_U64FromPtrDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); +DN_API DN_U64FromResult DN_U64FromPtr (void const *data, DN_USize size); +DN_API DN_U64 DN_U64FromPtrUnsafeDelimiter (void const *data, DN_USize size, DN_Str8 delimiter); +DN_API DN_U64 DN_U64FromPtrUnsafe (void const *data, DN_USize size); +DN_API DN_U64FromResult DN_U64FromHexPtr (void const *hex, DN_USize hex_count); +DN_API DN_U64 DN_U64FromHexPtrUnsafe (void const *hex, DN_USize hex_count); +DN_API DN_U64FromResult DN_U64FromHexStr8 (DN_Str8 hex); +DN_API DN_U64 DN_U64FromHexStr8Unsafe (DN_Str8 hex); +DN_API DN_U64 DN_U64FromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get U64 stored in big-endian at the high bytes [24:32) +DN_API DN_U64FromResult DN_U64FromU8x32HiBE (DN_U8x32 const *val); // Checks [0:24) bytes aren't set before getting the U64 +DN_API DN_USize DN_USizeFromU8x32HiBEUnsafe (DN_U8x32 const *val); // Get USize stored in big-endian at the high bytes [32 - sizeof USize:32) +DN_API DN_USizeFromResult DN_USizeFromU8x32HiBE (DN_U8x32 const *val); // Checks [0:sizeof USize) bytes aren't set before getting the U64 +DN_API DN_U32FromResult DN_U32FromHexStr8 (DN_Str8 hex); +DN_API DN_U32FromResult DN_U32FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_U32FromResult DN_U32FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_U32FromResult DN_U32FromPtr (void const *data, DN_USize size); +DN_API DN_I64FromResult DN_I64FromStr8Delimiters (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count); +DN_API DN_I64FromResult DN_I64FromStr8Delimiter (DN_Str8 string, DN_Str8 delimiter); +DN_API DN_I64FromResult DN_I64FromStr8 (DN_Str8 string); +DN_API DN_I64FromResult DN_I64FromPtr (void const *data, DN_USize size); +DN_API DN_I64 DN_I64FromPtrUnsafe (void const *data, DN_USize size); -DN_API bool DN_U8x32Eq (DN_U8x32 const *lhs, DN_U8x32 const *rhs); -DN_API DN_U8x32 DN_U8x32FromBytesLeftPadZ (DN_U8 const *ptr, DN_USize size); -DN_API DN_U8x32 DN_U8x32FromHexUnsafe (DN_Str8 hex_32b); -DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); -DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value +DN_API bool DN_U8x32Eq (DN_U8x32 const *lhs, DN_U8x32 const *rhs); +DN_API DN_U8x32 DN_U8x32FromBytesLeftPadZ (DN_U8 const *ptr, DN_USize size); +DN_API DN_U8x32 DN_U8x32FromHexUnsafe (DN_Str8 hex_32b); +DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); +DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value -DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); -DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); -DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); -DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize count, DN_U8 align, DN_ZMem z_mem); +DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); +DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); +DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); +DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize count, DN_U8 align, DN_ZMem z_mem); +#define DN_AllocatorNew(allocator, T, zmem) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), zmem) +#define DN_AllocatorNewZ(allocator, T) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), DN_ZMem_Yes) +#define DN_AllocatorNewNoZ(allocator, T) (T *)DN_AllocatorAlloc(allocator, sizeof(T), alignof(T), DN_ZMem_No) +#define DN_AllocatorNewArray(allocator, T, count, zmem) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), zmem) +#define DN_AllocatorNewArrayZ(allocator, T, count) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), DN_ZMem_Yes) +#define DN_AllocatorNewArrayNoZ(allocator, T, count) (T *)DN_AllocatorAlloc(allocator, sizeof(T) * (count), alignof(T), DN_ZMem_No) -DN_API DN_USize DN_FmtVCount (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_USize DN_FmtCount (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); -DN_API DN_FmtAppendResult DN_FmtAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...); -DN_API DN_FmtAppendResult DN_FmtAppendTruncate (char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...); -DN_API DN_USize DN_CStr8Count (char const *src); -DN_API DN_USize DN_CStr16Count (wchar_t const *src); +DN_API DN_USize DN_FmtVCount (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_USize DN_FmtCount (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); +DN_API DN_FmtAppendResult DN_FmtAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...); +DN_API DN_FmtAppendResult DN_FmtAppendTruncate (char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...); +DN_API DN_USize DN_CStr8Count (char const *src); +DN_API DN_USize DN_CStr16Count (wchar_t const *src); -#define DN_Str16Lit(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1} -#define DN_Str16FromPtr(data, count) DN_Literal(DN_Str16){(wchar_t *)(data), (DN_USize)(count)} +#define DN_Str16Lit(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1} +#define DN_Str16FromPtr(data, count) DN_Literal(DN_Str16){(wchar_t *)(data), (DN_USize)(count)} -#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} -#define DN_Str8PrintFmt(string) (int)((string).count), (string).data +#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} +#define DN_Str8PrintFmt(string) (int)((string).count), (string).data -#define DN_Str8FromPtr(data, count) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(count)} -#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->count) -#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) -DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize count, DN_ZMem z_mem, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8AllocArena (DN_USize count, DN_ZMem z_mem, DN_Arena *arena); -DN_API DN_Str8 DN_Str8AllocPool (DN_USize count, DN_Pool *pool); +#define DN_Str8FromPtr(data, count) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(count)} +#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->count) +#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) +DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize count, DN_ZMem z_mem, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8AllocArena (DN_USize count, DN_ZMem z_mem, DN_Arena *arena); +DN_API DN_Str8 DN_Str8AllocPool (DN_USize count, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); -DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize count, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize count, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); -DN_API DN_Str8 DN_Str8FmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); +DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize count, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize count, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x32 DN_Str8x32FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x64 DN_Str8x64FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x64 DN_Str8x64FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x128 DN_Str8x128FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x256 DN_Str8x256FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x512 DN_Str8x512FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x512 DN_Str8x512FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x1024 DN_Str8x1024FromFmt (DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8x1024 DN_Str8x1024FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x16AppendFmt (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x16AppendFmtV (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x32AppendFmt (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x32AppendFmtV (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x64AppendFmt (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x64AppendFmtV (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x128AppendFmt (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x128AppendFmtV (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x256AppendFmt (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x256AppendFmtV (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x512AppendFmt (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); -DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x32 DN_Str8x32FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x64 DN_Str8x64FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x64 DN_Str8x64FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x128 DN_Str8x128FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x256 DN_Str8x256FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x512 DN_Str8x512FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x512 DN_Str8x512FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8x1024 DN_Str8x1024FromFmt (DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8x1024 DN_Str8x1024FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x16AppendFmt (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x16AppendFmtV (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x32AppendFmt (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x32AppendFmtV (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x64AppendFmt (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x64AppendFmtV (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x128AppendFmt (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x128AppendFmtV (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x256AppendFmt (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x256AppendFmtV (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x512AppendFmt (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); -DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char seperator); -DN_API bool DN_Str8Is (DN_Str8 string, DN_Str8IsFlags flags); -DN_API char * DN_Str8End (DN_Str8 string); -DN_API DN_Str8 DN_Str8Subset (DN_Str8 string, DN_USize offset, DN_USize count); -DN_API DN_Str8 DN_Str8Advance (DN_Str8 string, DN_USize amount); -DN_API DN_Str8 DN_Str8NextLine (DN_Str8 string); -DN_API DN_Str8BSplitResult DN_Str8BSplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); -DN_API DN_Str8BSplitResult DN_Str8BSplit (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); -DN_API DN_Str8BSplitResult DN_Str8BSplitLast (DN_Str8 string, DN_Str8 find); -DN_API DN_USize DN_Str8Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags mode); -DN_API DN_Str8SplitResult DN_Str8SplitArena (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags mode, DN_Arena *arena); -DN_API DN_Str8FindResult DN_Str8FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case); -DN_API DN_Str8FindResult DN_Str8FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case); -DN_API DN_Str8FindResult DN_Str8Find (DN_Str8 string, DN_Str8FindFlag flags); -DN_API DN_Str8 DN_Str8Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); -DN_API DN_Str8 DN_Str8ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); -DN_API bool DN_Str8Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API bool DN_Str8EqSensitive (DN_Str8 lhs, DN_Str8 rhs); -DN_API bool DN_Str8EqInsensitive (DN_Str8 lhs, DN_Str8 rhs); -DN_API bool DN_Str8StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API bool DN_Str8StartsWithSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8StartsWithInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API bool DN_Str8EndsWithSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); +DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char seperator); +DN_API bool DN_Str8Is (DN_Str8 string, DN_Str8IsFlags flags); +DN_API char * DN_Str8End (DN_Str8 string); +DN_API DN_Str8 DN_Str8Subset (DN_Str8 string, DN_USize offset, DN_USize count); +DN_API DN_Str8 DN_Str8Advance (DN_Str8 string, DN_USize amount); +DN_API DN_Str8 DN_Str8NextLine (DN_Str8 string); +DN_API DN_Str8BSplitResult DN_Str8BSplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); +DN_API DN_Str8BSplitResult DN_Str8BSplit (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size); +DN_API DN_Str8BSplitResult DN_Str8BSplitLast (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags mode); +DN_API DN_Str8SplitResult DN_Str8SplitArena (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags mode, DN_Arena *arena); +DN_API DN_Str8FindResult DN_Str8FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case); +DN_API DN_Str8FindResult DN_Str8FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case); +DN_API DN_Str8FindResult DN_Str8Find (DN_Str8 string, DN_Str8FindFlag flags); +DN_API DN_Str8 DN_Str8Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); +DN_API DN_Str8 DN_Str8ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char); +DN_API bool DN_Str8Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API bool DN_Str8EqSensitive (DN_Str8 lhs, DN_Str8 rhs); +DN_API bool DN_Str8EqInsensitive (DN_Str8 lhs, DN_Str8 rhs); +DN_API bool DN_Str8StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API bool DN_Str8StartsWithSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8StartsWithInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API bool DN_Str8EndsWithSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); -DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimPrefixSensitive (DN_Str8 string, DN_Str8 prefix); -DN_API DN_Str8 DN_Str8TrimPrefixInsensitive (DN_Str8 string, DN_Str8 prefix); -DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimSuffixSensitive (DN_Str8 string, DN_Str8 suffix); -DN_API DN_Str8 DN_Str8TrimSuffixInsensitive (DN_Str8 string, DN_Str8 suffix); -DN_API DN_Str8 DN_Str8TrimAround (DN_Str8 string, DN_Str8 trim_string, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8TrimAroundSensitive (DN_Str8 string, DN_Str8 trim_string); -DN_API DN_Str8 DN_Str8TrimAroundInsensitive (DN_Str8 string, DN_Str8 trim_string); -DN_API DN_Str8 DN_Str8TrimHeadWhitespace (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); -DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimPrefixSensitive (DN_Str8 string, DN_Str8 prefix); +DN_API DN_Str8 DN_Str8TrimPrefixInsensitive (DN_Str8 string, DN_Str8 prefix); +DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimSuffixSensitive (DN_Str8 string, DN_Str8 suffix); +DN_API DN_Str8 DN_Str8TrimSuffixInsensitive (DN_Str8 string, DN_Str8 suffix); +DN_API DN_Str8 DN_Str8TrimAround (DN_Str8 string, DN_Str8 trim_string, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8TrimAroundSensitive (DN_Str8 string, DN_Str8 trim_string); +DN_API DN_Str8 DN_Str8TrimAroundInsensitive (DN_Str8 string, DN_Str8 trim_string); +DN_API DN_Str8 DN_Str8TrimHeadWhitespace (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); +DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); -DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileExtension (DN_Str8 path); -DN_API DN_Str8 DN_Str8FileDirectoryFromPath (DN_Str8 path); -DN_API DN_Str8 DN_Str8AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...); -DN_API DN_Str8 DN_Str8AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...); -DN_API DN_Str8 DN_Str8FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args); -DN_API void DN_Str8Remove (DN_Str8 *string, DN_USize offset, DN_USize count); -DN_API DN_Str8 DN_Str8TruncateArena (DN_Str8 string, DN_USize max_count, DN_Str8 truncator, DN_Arena *arena); -DN_API DN_Str8TruncResult DN_Str8TruncMiddlePtr (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, char *dest, DN_USize dest_max); -DN_API DN_Str8TruncResult DN_Str8TruncMiddle (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Lower (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Upper (DN_Str8 string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); -DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); -DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileExtension (DN_Str8 path); +DN_API DN_Str8 DN_Str8FileDirectoryFromPath (DN_Str8 path); +DN_API DN_Str8 DN_Str8AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...); +DN_API DN_Str8 DN_Str8AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...); +DN_API DN_Str8 DN_Str8FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args); +DN_API void DN_Str8Remove (DN_Str8 *string, DN_USize offset, DN_USize count); +DN_API DN_Str8 DN_Str8TruncateArena (DN_Str8 string, DN_USize max_count, DN_Str8 truncator, DN_Arena *arena); +DN_API DN_Str8TruncResult DN_Str8TruncMiddlePtr (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, char *dest, DN_USize dest_max); +DN_API DN_Str8TruncResult DN_Str8TruncMiddle (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Lower (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Upper (DN_Str8 string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); +DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); +DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); +DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); #if DN_WITH_STR8_AVX512F -DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); -DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); #endif -DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 seperator, DN_Arena *arena); -DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); -DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 seperator, DN_Arena *arena); +DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); +DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); -DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); -DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 seperator, DN_Arena *arena); -DN_API DN_Str16 DN_Str16RenderSpaceSep (DN_Str16Slice array, DN_Arena *arena); +DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); +DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 seperator, DN_Arena *arena); +DN_API DN_Str16 DN_Str16RenderSpaceSep (DN_Str16Slice array, DN_Arena *arena); -DN_API DN_Str8Builder DN_Str8BuilderFromArena (DN_Arena *arena); -DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); -DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); -DN_API DN_Str8Builder DN_Str8BuilderFromBuilder (DN_Arena *arena, DN_Str8Builder const *builder); -DN_API bool DN_Str8BuilderAddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); -DN_API bool DN_Str8BuilderAddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); -DN_API bool DN_Str8BuilderAddFV (DN_Str8Builder *builder, DN_AddType add, DN_FMT_ATTRIB char const *fmt, va_list args); -#define DN_Str8BuilderAppendArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Append) -#define DN_Str8BuilderAppendArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Append) -#define DN_Str8BuilderAppendSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Append) -#define DN_Str8BuilderAppendSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Append) -DN_API bool DN_Str8BuilderAppendRef (DN_Str8Builder *builder, DN_Str8 string); -DN_API bool DN_Str8BuilderAppendCopy (DN_Str8Builder *builder, DN_Str8 string); -#define DN_Str8BuilderAppendFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Append, fmt, args) -DN_API bool DN_Str8BuilderAppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_Str8BuilderAppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize count); -DN_API bool DN_Str8BuilderAppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize count); -DN_API bool DN_Str8BuilderAppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src); -DN_API bool DN_Str8BuilderAppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src); -#define DN_Str8BuilderPrependArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Prepend) -#define DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Prepend) -#define DN_Str8BuilderPrependSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Prepend) -#define DN_Str8BuilderPrependSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Prepend) -DN_API bool DN_Str8BuilderPrependRef (DN_Str8Builder *builder, DN_Str8 string); -DN_API bool DN_Str8BuilderPrependCopy (DN_Str8Builder *builder, DN_Str8 string); -#define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Prepend, fmt, args) -DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); -DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); -DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); +DN_API DN_Str8Builder DN_Str8BuilderFromArena (DN_Arena *arena); +DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); +DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize count); +DN_API DN_Str8Builder DN_Str8BuilderFromBuilder (DN_Arena *arena, DN_Str8Builder const *builder); +DN_API bool DN_Str8BuilderAddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); +DN_API bool DN_Str8BuilderAddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add); +DN_API bool DN_Str8BuilderAddFV (DN_Str8Builder *builder, DN_AddType add, DN_FMT_ATTRIB char const *fmt, va_list args); +#define DN_Str8BuilderAppendArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Append) +#define DN_Str8BuilderAppendArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Append) +#define DN_Str8BuilderAppendSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Append) +#define DN_Str8BuilderAppendSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Append) +DN_API bool DN_Str8BuilderAppendRef (DN_Str8Builder *builder, DN_Str8 string); +DN_API bool DN_Str8BuilderAppendCopy (DN_Str8Builder *builder, DN_Str8 string); +#define DN_Str8BuilderAppendFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Append, fmt, args) +DN_API bool DN_Str8BuilderAppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_Str8BuilderAppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize count); +DN_API bool DN_Str8BuilderAppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize count); +DN_API bool DN_Str8BuilderAppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src); +DN_API bool DN_Str8BuilderAppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src); +#define DN_Str8BuilderPrependArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Prepend) +#define DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Prepend) +#define DN_Str8BuilderPrependSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Prepend) +#define DN_Str8BuilderPrependSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_Prepend) +DN_API bool DN_Str8BuilderPrependRef (DN_Str8Builder *builder, DN_Str8 string); +DN_API bool DN_Str8BuilderPrependCopy (DN_Str8Builder *builder, DN_Str8 string); +#define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_AddType_Prepend, fmt, args) +DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); -DN_API bool DN_PathAddRef (DN_Path *path, DN_Str8 add, DN_Arena *arena); -DN_API bool DN_PathAdd (DN_Path *path, DN_Str8 add, DN_Arena *arena); -DN_API bool DN_PathAddF (DN_Path *path, DN_FMT_ATTRIB char const *fmt, ...); -DN_API bool DN_PathPop (DN_Path *path); -DN_API DN_Str8 DN_Str8FromPath (DN_Path const *path, DN_Str8 path_seperator, DN_Allocator allocator); -DN_API DN_Str8 DN_Str8FmtPathVArena (DN_Str8 path_seperator, DN_Arena *arena, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPathArena (DN_Str8 path_seperator, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtPathVPool (DN_Str8 path_seperator, DN_Pool *pool, char const *fmt, va_list args); -DN_API DN_Str8 DN_Str8FmtPathPool (DN_Str8 path_seperator, DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtOsPathArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FmtOsPathPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_PathAddRef (DN_Path *path, DN_Str8 add, DN_Arena *arena); +DN_API bool DN_PathAdd (DN_Path *path, DN_Str8 add, DN_Arena *arena); +DN_API bool DN_PathAddF (DN_Path *path, DN_FMT_ATTRIB char const *fmt, ...); +DN_API bool DN_PathPop (DN_Path *path); +DN_API DN_Str8 DN_Str8FromPath (DN_Path const *path, DN_Str8 path_seperator, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FmtPathVArena (DN_Str8 path_seperator, DN_Arena *arena, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPathArena (DN_Str8 path_seperator, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtPathVPool (DN_Str8 path_seperator, DN_Pool *pool, char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FmtPathPool (DN_Str8 path_seperator, DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtOsPathArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); +DN_API DN_Str8 DN_Str8FmtOsPathPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_USize DN_Utf8FromUtf32 (DN_U8 utf8[4], DN_U32 codepoint); -DN_API bool DN_Utf32IsAlphabet (DN_U32 codepoint); -DN_API bool DN_Utf32IsDigit (DN_U32 codepoint); -DN_API bool DN_Utf32IsAlphaNum (DN_U32 codepoint); -DN_API bool DN_Utf32IsWhitespace (DN_U32 codepoint); -DN_API bool DN_Utf32IsHex (DN_U32 codepoint); -DN_API bool DN_Utf32IsWordCharacter (DN_U32 codepoint); -DN_API DN_U32 DN_Utf32ToLower (DN_U32 codepoint); -DN_API DN_U32 DN_Utf32ToUpper (DN_U32 codepoint); -DN_API DN_USize DN_Utf32FromUtf16 (DN_U16 utf16[2], DN_U32 codepoint); -DN_API DN_Utf32FromResult DN_Utf32FromUtf8Stream (DN_Str8 stream); -DN_API bool DN_Utf32FromUtf8Iterate (DN_Utf32FromUtf8Iterator *it, DN_Utf32IterateDir dir, DN_Str8 utf8); -DN_API DN_USize DN_Utf32CodepointCountFromUtf8 (DN_Str8 str, DN_CodepointCountFlags flags); +DN_API DN_USize DN_Utf8FromUtf32 (DN_U8 utf8[4], DN_U32 codepoint); +DN_API bool DN_Utf32IsAlphabet (DN_U32 codepoint); +DN_API bool DN_Utf32IsDigit (DN_U32 codepoint); +DN_API bool DN_Utf32IsAlphaNum (DN_U32 codepoint); +DN_API bool DN_Utf32IsWhitespace (DN_U32 codepoint); +DN_API bool DN_Utf32IsHex (DN_U32 codepoint); +DN_API bool DN_Utf32IsWordCharacter (DN_U32 codepoint); +DN_API DN_U32 DN_Utf32ToLower (DN_U32 codepoint); +DN_API DN_U32 DN_Utf32ToUpper (DN_U32 codepoint); +DN_API DN_USize DN_Utf32FromUtf16 (DN_U16 utf16[2], DN_U32 codepoint); +DN_API DN_Utf32FromResult DN_Utf32FromUtf8Stream (DN_Str8 stream); +DN_API bool DN_Utf32FromUtf8Iterate (DN_Utf32FromUtf8Iterator *it, DN_Utf32IterateDir dir, DN_Str8 utf8); +DN_API DN_USize DN_Utf32CodepointCountFromUtf8 (DN_Str8 str, DN_CodepointCountFlags flags); -DN_API DN_U8 DN_U8FromHexNibble (char hex); -DN_API DN_NibbleFromU8Result DN_NibbleFromU8 (DN_U8 u8); +DN_API DN_U8 DN_U8FromHexNibble (char hex); +DN_API DN_NibbleFromU8Result DN_NibbleFromU8 (DN_U8 u8); -DN_API DN_USize DN_PtrBytesFromStr8Hex (DN_Str8 hex, void *dest, DN_USize dest_count); -DN_API DN_USize DN_PtrBytesFromPtrHex (char const *hex, DN_USize hex_count, void *dest, DN_USize dest_count); -DN_API DN_Str8 DN_Str8BytesFromStr8HexArena (DN_Str8 hex, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BytesFromPtrHexArena (char const *hex, DN_USize hex_count, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BytesFromPtrHexPool (char const *hex, DN_USize hex_count, DN_Pool *pool); -DN_API DN_U8x16 DN_U8x16FromPtrHex32 (char const *hex, DN_USize hex_count); -DN_API DN_U8x32 DN_U8x32FromPtrHex64 (char const *hex, DN_USize hex_count); +DN_API DN_USize DN_PtrBytesFromStr8Hex (DN_Str8 hex, void *dest, DN_USize dest_count); +DN_API DN_USize DN_PtrBytesFromPtrHex (char const *hex, DN_USize hex_count, void *dest, DN_USize dest_count); +DN_API DN_Str8 DN_Str8BytesFromStr8HexArena (DN_Str8 hex, DN_Arena *arena); +DN_API DN_Str8 DN_Str8BytesFromPtrHexArena (char const *hex, DN_USize hex_count, DN_Arena *arena); +DN_API DN_Str8 DN_Str8BytesFromPtrHexPool (char const *hex, DN_USize hex_count, DN_Pool *pool); +DN_API DN_U8x16 DN_U8x16FromPtrHex32 (char const *hex, DN_USize hex_count); +DN_API DN_U8x32 DN_U8x32FromPtrHex64 (char const *hex, DN_USize hex_count); -DN_API DN_HexU64 DN_HexU64FromU64 (DN_U64 value, DN_HexFromU64Type type); -DN_API DN_USize DN_PtrHexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_USize DN_PtrHexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8 DN_Str8HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8 DN_Str8HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex32 DN_Hex32FromPtrBytes16 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex64 DN_Hex64FromPtrBytes32 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex64 DN_Hex64FromU8x32 (DN_U8x32 const *value, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Hex128 DN_Hex128FromPtrBytes64 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_HexU64 DN_HexU64FromU64 (DN_U64 value, DN_HexFromU64Type type); +DN_API DN_USize DN_PtrHexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_USize DN_PtrHexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_Str8HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_Str8HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex32 DN_Hex32FromPtrBytes16 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex64 DN_Hex64FromPtrBytes32 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex64 DN_Hex64FromU8x32 (DN_U8x32 const *value, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Hex128 DN_Hex128FromPtrBytes64 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); -DN_API DN_Str8x128 DN_AgeStr8FromMsU64 (DN_U64 duration_ms, DN_AgeUnit units); -DN_API DN_Str8x128 DN_AgeStr8FromSecU64 (DN_U64 duration_ms, DN_AgeUnit units); -DN_API DN_Str8x128 DN_AgeStr8FromSecF64 (DN_F64 sec, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromMsU64 (DN_U64 duration_ms, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromSecU64 (DN_U64 duration_ms, DN_AgeUnit units); +DN_API DN_Str8x128 DN_AgeStr8FromSecF64 (DN_F64 sec, DN_AgeUnit units); -DN_API int DN_IsLeapYear (int year); -DN_API bool DN_DateIsValid (DN_Date date); -DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); -DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); +DN_API int DN_IsLeapYear (int year); +DN_API bool DN_DateIsValid (DN_Date date); +DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); +DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); -DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); -DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); -DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); -#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) +DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); +DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); +#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) // NOTE: Profiler // Overview @@ -3361,6 +3513,17 @@ DN_API void DN_ProfilerFmtToStdout DN_API DN_F64 DN_ProfilerSecFromTsc (DN_Profiler *profiler, DN_U64 duration_tsc); DN_API DN_F64 DN_ProfilerMsFromTsc (DN_Profiler *profiler, DN_U64 duration_tsc); +DN_API DN_Profiler2 DN_Profiler2Init (DN_ProfilerTscNowFunc *tsc_now_func, DN_U64 tsc_frequency, DN_Profiler2Marker *markers, DN_U32 markers_count, DN_Profiler2Frame *frames, DN_U32 frames_count); +DN_API void DN_Profiler2FrameBegin (DN_Profiler2 *profiler); +DN_API DN_Profiler2Zone DN_Profiler2ZoneBegin (DN_Profiler2 *profiler, DN_Str8 name, DN_CallSite call_site); +DN_API void DN_Profiler2ZoneEnd (DN_Profiler2Zone *zone); +#define DN_Profiler2ZoneBeginName(profiler, name_cstr) DN_Profiler2ZoneBegin(profiler, DN_Str8Lit(name_cstr), DN_CallSiteNow) +#define DN_Profiler2ZoneBeginUnnamed(profiler) DN_Profiler2ZoneBegin(profiler, DN_Str8Lit(""), DN_CallSiteNow) +#define DN_Profiler2ZoneScopeName(profiler, name_cstr) DN_Profiler2Zone DN_UniqueName(zone) = DN_Profiler2ZoneBeginName(profiler, name_cstr); !DN_UniqueName(zone).scope_once; DN_Profiler2ZoneEnd(DN_UniqueName(&zone)) +DN_API bool DN_Profiler2MarkerWalkDfsPreOrder (DN_Profiler2 *profiler, DN_Profiler2Iterator *it); +DN_API DN_V2USize DN_Profiler2MarkerOffsetFromFrame (DN_Profiler2 const *profiler, DN_Profiler2Frame const *frame); +DN_API DN_Profiler2Marker* DN_Profiler2MarkerFromFrameIndex (DN_Profiler2 *profiler, DN_Profiler2Frame *frame, DN_U32 index); + DN_API void DN_QSort_ (void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare); #if defined(__cplusplus) template void DN_QSortT (T *array, DN_USize array_size, void *user_context, DN_QSortCompareFunc *compare); @@ -3408,16 +3571,12 @@ DN_API void DN_Pcg32Advance DN_API DN_U32 DN_Fnv1aHashU32FromBytes (void const *bytes, DN_USize count, DN_U32 seed); DN_API DN_U64 DN_Fnv1aHashU64FromBytes (void const *bytes, DN_USize count, DN_U64 seed); -DN_API DN_U32 DN_Murmur3HashU32FromBytesX86 (void const *bytes, int len, DN_U32 seed); -DN_API DN_Murmur3 DN_Murmur3HashU128FromBytesX64 (void const *bytes, int len, DN_U32 seed); -DN_API DN_U64 DN_Murmur3HashU64FromBytesX64 (void const *bytes, int len, DN_U32 seed); -DN_API DN_U32 DN_Murmur3HashU32FromBytesX64 (void const *bytes, int len, DN_U32 seed); - -#if defined(DN_64_BIT) - #define DN_Murmur3HashU32FromBytes(bytes, len, seed) DN_Murmur3HashU32FromBytesX64(bytes, len, seed) -#else - #define DN_Murmur3HashU32FromBytes(bytes, len, seed) DN_Murmur3HashU32FromBytesX86(bytes, len, seed) -#endif +DN_API void DN_Murmur3HashRawProcess (DN_U32 *ph1, DN_U32 *pcarry, const void *key, int len); +DN_API DN_U32 DN_Murmur3HashRawResult (DN_U32 h1, DN_U32 carry, DN_U32 total_length); +DN_API DN_U32 DN_Murmur3Hash (DN_U32 seed, const void *key, int len); +DN_API DN_Murmur3Context DN_Murmur3HashInit (DN_U32 seed); +DN_API void DN_Murmur3HashUpdate (DN_Murmur3Context *ctx, const void *key, int len); +DN_API DN_U32 DN_Murmur3HashFinish (DN_Murmur3Context const *ctx); #define DN_AnsiCodeBoldLit "\x1b[1m" #define DN_AnsiCodeResetLit "\x1b[0m" @@ -3668,17 +3827,17 @@ DN_API DN_V3F32 DN_V3F32Normalise #define DN_V4F32From1N(x) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}} #define DN_V4F32From4N(x, y, z, w) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}} -#define DN_V4F32FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{xyz.x, xyz.y, xyz.z, w}} -#define DN_V4F32Rgba01FromRgbaU8(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}} -#define DN_V4F32Rgba01FromRgbU8(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}} +#define DN_V4F32FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{(xyz).x, (xyz).y, (xyz).z, (w)}} +#define DN_V4F32FromRgba255ToRgba01(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}} +#define DN_V4F32FromRgb255ToRgba01(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}} DN_API DN_V4F32 DN_V4F32Lerp (DN_V4F32 lhs, DN_F32 t01, DN_V4F32 rhs); DN_API bool DN_V4F32Rgba01IsValid (DN_V4F32 rgba01); -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbU32 (DN_U32 u32); -DN_API DN_V4F32 DN_V4F32Rgba01FromRgbaU32 (DN_U32 u32); -DN_API DN_V4F32 DN_V4F32Linear01FromSrgb01 (DN_V4F32 rgb01); +DN_API DN_V4F32 DN_V4F32FromRgbU32ToRgba01 (DN_U32 u32); +DN_API DN_V4F32 DN_V4F32FromRgbaU32ToRgba01 (DN_U32 u32); +DN_API DN_V4F32 DN_V4F32FromSrgb01ToLinear01 (DN_V4F32 rgb01); DN_API DN_V4F32 DN_V4F32Linear01Desaturate (DN_V4F32 linear01, DN_F32 t01); -DN_API DN_V4F32 DN_V4F32Srgb01FromLinear01 (DN_V4F32 linear01); +DN_API DN_V4F32 DN_V4F32FromLinear01ToSrgb01 (DN_V4F32 linear01); #define DN_V4F32FromV4Alpha(v4, alpha) DN_V4F32FromV3And1N(v4.xyz, alpha) @@ -3723,6 +3882,7 @@ DN_API DN_Str8x256 DN_M4ColumnMajorString DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API DN_M2x3 DN_M2x3Identity (); +DN_API bool DN_M2x3IsIdentity (DN_M2x3 m2x3); DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset); DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3); DN_API DN_M2x3 DN_M2x3Scale (DN_V2F32 scale); @@ -3778,50 +3938,175 @@ DN_API DN_Rect DN_RectCutCut DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 (DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b); -// NOTE: Containers that are imlpemented using primarily macros for operating on data structures +// NOTE: Containers that are implemented using primarily macros for operating on data structures // that are embedded into a C style struct or from a set of defined variables from the available // scope. Keep it stupid simple, structs and functions. Minimal amount of container types with // flexible construction leads to less duplicated container code and less template meta-programming. -// NOTE: Intrusive Singly Linked List -// Define a struct with the members `next`: +// NOTE: Lists (Linked, Queue, Stack) // -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// } my_link = {}; -// -// MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem); -#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) +// 1. Fully double-linked with first and last pointers (linked list) +// 2. Singly linked with first and last pointers from the list head (queue list) +// 3. Singly linked with just a single first pointer from the header (stack list) -// NOTE: Singly Linked List with Head and Tail pointer -/* - struct MyLinkItem { - int data; - MyLinkItem *next; - } my_list = {}; +#if defined(NDEBUG) + #define DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, check_ptr) +#else + #define DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, check_ptr) \ + bool DN_UniqueName(dbg_verify_pointer_in_list) = false; \ + for (Type *dbg_it_ = first_ptr; dbg_it_; dbg_it_ = dbg_it_->field_for_next) { \ + if (dbg_it_ == check_ptr) { \ + DN_UniqueName(dbg_verify_pointer_in_list) = true; \ + break; \ + } \ + } \ + DN_AssertF(DN_UniqueName(dbg_verify_pointer_in_list), "Pointer %p is not a part of the linked list at %p", check_ptr, first_ptr); +#endif - struct MyContainer { - MyLinkItem *head; - MyLinkItem *tail; - }; +#define DN_ForList(Type, var_it_name, first_ptr, field_for_next) \ + Type *var_it_name = (first_ptr), *var_it_name##_next = nullptr; (var_it_name ? (var_it_name##_next = var_it_name->field_for_next, 1) : 0); var_it_name = var_it_name##_next - MyLinkItem item = {}; - MyContainer container = {}; - DN_ISinglyHeadTailLLAppend(container, item); - // ... or alternatively, DN_SinglyHeadTailLLAppend(container.head, container.tail, item); - - for (MyLinkItem *it = container.head; it; it = it->next) { } -*/ -#define DN_SinglyHeadTailLLAppend(head, tail, to_append) \ - do { \ - if (!head) \ - head = to_append; \ - if (tail) \ - tail->next = to_append; \ - tail = to_append; \ +#define DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, add) \ + do { \ + if (first_ptr) { \ + DN_AssertF(last_ptr, "Invalid list: first is set (%p) but last is null", first_ptr); \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, last_ptr); \ + } else { \ + DN_AssertF(!last_ptr, "Invalid list: first is null but last is set (%p)"); \ + } \ + DN_AssertF(!item_to_add_ptr->field_for_next, "Invalid list: pointer to add already has next set (%p), should be null", item_to_add_ptr->field_for_next); \ + if (add == DN_AddType_Append) { \ + if (!first_ptr) \ + first_ptr = item_to_add_ptr; \ + if (last_ptr) \ + last_ptr->field_for_next = item_to_add_ptr; \ + last_ptr = item_to_add_ptr; \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + item_to_add_ptr->field_for_next = first_ptr; \ + first_ptr = item_to_add_ptr; \ + if (!last_ptr) \ + last_ptr = item_to_add_ptr; \ + } \ } while (0) -#define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append) + +#define DN_ListQueueAppend(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr) DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, DN_AddType_Append) +#define DN_ListQueuePrepend(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr) DN_ListQueueAdd(Type, first_ptr, last_ptr, field_for_next, item_to_add_ptr, DN_AddType_Prepend) + +#define DN_ListQueueDetach(Type, first_ptr, last_ptr, field_for_next, detach_ptr) \ + do { \ + if ((detach_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_ptr); \ + if ((detach_ptr) == (first_ptr)) { \ + if ((first_ptr) == (last_ptr)) \ + (last_ptr) = nullptr; \ + (first_ptr) = (detach_ptr)->field_for_next; \ + } else { \ + Type *prev_ = (first_ptr); \ + while (prev_ && prev_->field_for_next != (detach_ptr)) \ + prev_ = prev_->field_for_next; \ + prev_->field_for_next = (detach_ptr)->field_for_next; \ + if ((detach_ptr) == (last_ptr)) \ + (last_ptr) = prev_; \ + } \ + (detach_ptr)->field_for_next = nullptr; \ + } \ + } while (0) + +#define DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, add) \ + do { \ + if ((first_ptr)) { \ + DN_AssertF((last_ptr), "Invalid list: first is set (%p) but last is null", (first_ptr)); \ + } else { \ + DN_AssertF(!(last_ptr), "Invalid list: first is null but last is set (%p)", (last_ptr)); \ + (first_ptr) = (dest_ptr); \ + (last_ptr) = (dest_ptr); \ + DN_AssertF(!(dest_ptr)->field_for_next, "Invalid list: First and last is being initialised to dest (%p) but dest has next (%p) set, dest cannot be assigned as the last pointer", (dest_ptr), (dest_ptr)->field_for_next); \ + DN_AssertF(!(dest_ptr)->field_for_prev, "Invalid list: First and last is being initialised to dest (%p) but dest has prev (%p) set, dest cannot be assigned as the first pointer", (dest_ptr), (dest_ptr)->field_for_prev); \ + } \ + if ((src_ptr)) { \ + if (add == DN_AddType_Append) { \ + (src_ptr)->field_for_next = (dest_ptr) ? (dest_ptr)->field_for_next : nullptr; \ + (src_ptr)->field_for_prev = (dest_ptr); \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + (src_ptr)->field_for_next = (dest_ptr); \ + (src_ptr)->field_for_prev = (dest_ptr) ? (dest_ptr)->field_for_prev : nullptr; \ + } \ + } \ + if ((dest_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, dest_ptr); \ + if (add == DN_AddType_Append) { \ + if ((dest_ptr)->field_for_next) \ + (dest_ptr)->field_for_next->field_for_prev = (src_ptr); \ + (dest_ptr)->field_for_next = (src_ptr); \ + } else { \ + DN_Assert(add == DN_AddType_Prepend); \ + if ((dest_ptr)->field_for_prev) \ + (dest_ptr)->field_for_prev->field_for_next = (src_ptr); \ + (dest_ptr)->field_for_prev = (src_ptr); \ + } \ + } \ + if ((dest_ptr) == (first_ptr) && add == DN_AddType_Prepend) \ + (first_ptr) = (src_ptr); \ + if ((dest_ptr) == (last_ptr) && add == DN_AddType_Append) \ + (last_ptr) = (src_ptr); \ + } while (0) + +#define DN_ListLinkedAppendTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, DN_AddType_Append) +#define DN_ListLinkedPrependTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, dest_ptr, DN_AddType_Prepend) +#define DN_ListLinkedAppend(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, last_ptr, DN_AddType_Append) +#define DN_ListLinkedPrepend(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr) DN_ListLinkedAddTo(Type, first_ptr, last_ptr, field_for_next, field_for_prev, src_ptr, first_ptr, DN_AddType_Prepend) +#define DN_ListLinkedDetach(Type, first_ptr, last_ptr, field_for_next, field_for_prev, detach_ptr) \ + do { \ + if ((first_ptr) && (last_ptr) && (detach_ptr)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_ptr); \ + if ((detach_ptr) == (first_ptr)) \ + (first_ptr) = (detach_ptr)->field_for_next; \ + if ((detach_ptr) == (last_ptr)) \ + (last_ptr) = (detach_ptr)->field_for_prev; \ + if ((detach_ptr)->field_for_prev) \ + (detach_ptr)->field_for_prev->field_for_next = (detach_ptr)->field_for_next; \ + if ((detach_ptr)->field_for_next) \ + (detach_ptr)->field_for_next->field_for_prev = (detach_ptr)->field_for_prev; \ + (detach_ptr)->field_for_next = nullptr; \ + (detach_ptr)->field_for_prev = nullptr; \ + } \ + } while (0) + +#define DN_ListStackPush(Type, first_ptr, field_for_next, item_ptr) \ + do { \ + DN_AssertF(!(item_ptr)->field_for_next, \ + "Invalid list: pointer to push already has next set (%p), should be null", \ + (void *)(item_ptr)->field_for_next); \ + (item_ptr)->field_for_next = (first_ptr); \ + (first_ptr) = (item_ptr); \ + } while (0) + +#define DN_ListStackDetach(Type, first_ptr, field_for_next, detach_ptr) \ + do { \ + /* NOTE: `detach_` caches `detach_ptr` into a local because `detach_ptr` can alias `first_ptr`. \ + DN_ListStackPop() passes `first_ptr` as both arguments and mutates `first_ptr` which would \ + also modify `detach_ptr` causing unwanted side-effects like a null-ptr deref. */ \ + Type *detach_ = (detach_ptr); \ + if ((first_ptr) && (detach_)) { \ + DN_ListDebugVerifyListContainsPtr(Type, first_ptr, field_for_next, detach_); \ + if (detach_ == (first_ptr)) { \ + (first_ptr) = detach_->field_for_next; \ + } else { \ + Type *prev_ = (first_ptr); \ + while (prev_ && prev_->field_for_next != detach_) \ + prev_ = prev_->field_for_next; \ + DN_Assert(prev_); \ + if (prev_) \ + prev_->field_for_next = detach_->field_for_next; \ + } \ + detach_->field_for_next = nullptr; \ + } \ + } while (0) + +#define DN_ListStackPop(Type, first_ptr, field_for_next) \ + DN_ListStackDetach(Type, first_ptr, field_for_next, first_ptr) // NOTE: Sentinel Doubly Linked List // Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. @@ -4294,7 +4579,6 @@ template DN_USize DN_TArrayCopyPtrPool (T **da template DN_USize DN_TArrayCopyPtrPoolAssert (T **data, T const *src, DN_USize count, DN_Pool *pool, DN_CallSite call_site); #endif -DN_API void* DN_SinglyLLDetach (void **link, void **next); DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); @@ -4558,21 +4842,23 @@ DN_API void DN_TestVerifyBytesF #define DN_TestVerifyBytesNotEq(test_ptr, bytes, expect) DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " != " #expect), (bytes), (expect), /*expect_eq=*/ false, "") #if DN_WITH_TESTS -DN_API DN_TestCore DN_TestSuite (DN_Arena *arena_); +DN_API DN_TestCore DN_TestSuite (DN_Arena *arena_); #endif -DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); -DN_API void DN_LeakTrackDealloc_ (DN_LeakTracker *leak, void *ptr); -DN_API void DN_LeakDump_ (DN_LeakTracker *leak); +DN_API DN_MemDebuggerSnapshot DN_MemDebuggerSnapshotMake (DN_MemDebugger *debugger, DN_Arena *arena); +DN_API bool DN_MemDebuggerSnapshotPtrDataQSortComparePtrLT (void const *lhs, void const *rhs, void*); +DN_API void DN_MemDebuggerAlloc_ (DN_MemDebugger *debugger, void *ptr, DN_USize size, bool alloc_can_leak, DN_CallSite call_site); +DN_API void DN_MemDebuggerDealloc_ (DN_MemDebugger *debugger, void *ptr); +DN_API void DN_MemDebuggerDump_ (DN_MemDebugger *debugger); -#if defined(DN_LEAK_TRACKING) -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) -#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) -#define DN_LeakDump(leak) DN_LeakDump_(leak) +#if defined(DN_MEM_DEBUGGER) +#define DN_MemDebuggerAlloc(debugger, ptr, size, alloc_can_leak, call_site) DN_MemDebuggerAlloc_(debugger, ptr, size, alloc_can_leak, call_site) +#define DN_MemDebuggerDealloc(debugger, ptr) DN_MemDebuggerDealloc_(debugger, ptr) +#define DN_MemDebuggerDump(debugger) DN_MemDebuggerDump_(debugger) #else -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) -#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) -#define DN_LeakDump(leak) do { } while (0) +#define DN_MemDebuggerAlloc(debugger, ptr, size, alloc_can_leak, call_site) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) +#define DN_MemDebuggerDealloc(debugger, ptr) do { (void)ptr; } while (0) +#define DN_MemDebuggerDump(debugger) do { } while (0) #endif #if DN_WITH_OS @@ -4633,55 +4919,57 @@ DN_API void DN_OS_LoggerF // always unless you are on Emscripten where it'll use basic allocator (virtual memory is not // supported through Emscripten). -DN_API void * DN_OS_HeapBasicAlloc (DN_USize size); -DN_API void DN_OS_HeapBasicDealloc (void *ptr); -DN_API DN_Heap DN_OS_HeapInitBasic (); -DN_API DN_Heap DN_OS_HeapInitVirtual (); -DN_API DN_Heap DN_OS_HeapInitDefault (); +DN_API void * DN_OS_HeapBasicAlloc (DN_USize size); +DN_API void DN_OS_HeapBasicDealloc (void *ptr); +DN_API DN_Heap DN_OS_HeapInitBasic (); +DN_API DN_Heap DN_OS_HeapInitVirtual (); +DN_API DN_Heap DN_OS_HeapInitDefault (); -DN_API DN_Arena DN_OS_ArenaFromHeapVirtual (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); -DN_API DN_Arena DN_OS_ArenaFromHeapBasic (DN_U64 size, DN_MemFlags flags); +DN_API DN_Arena DN_OS_ArenaFromHeapVirtualCallSite (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); +#define DN_OS_ArenaFromHeapVirtual(reserve, commit, flags, name_cstr) DN_OS_ArenaFromHeapVirtualCallSite(reserve, commit, flags, DN_CallSiteNowNamed(name_cstr)) +DN_API DN_Arena DN_OS_ArenaFromHeapBasicCallSite (DN_U64 size, DN_MemFlags flags); +#define DN_OS_ArenaFromHeapBasic(size, flags, name_cstr) DN_OS_ArenaFromHeapBasic(size, flags, DN_CallSiteNowNamed(name_cstr)) -DN_API void * DN_OS_MemReserve (DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); -DN_API bool DN_OS_MemCommit (void *ptr, DN_USize count, DN_U32 page_flags); -DN_API void DN_OS_MemDecommit (void *ptr, DN_USize count); -DN_API void DN_OS_MemRelease (void *ptr, DN_USize count); -DN_API int DN_OS_MemProtect (void *ptr, DN_USize count, DN_U32 page_flags); +DN_API void * DN_OS_MemReserve (DN_USize count, DN_MemCommit commit, DN_MemPage page_flags); +DN_API bool DN_OS_MemCommit (void *ptr, DN_USize count, DN_U32 page_flags); +DN_API void DN_OS_MemDecommit (void *ptr, DN_USize count); +DN_API void DN_OS_MemRelease (void *ptr, DN_USize count); +DN_API int DN_OS_MemProtect (void *ptr, DN_USize count, DN_U32 page_flags); -DN_API void * DN_OS_MemAlloc (DN_USize count, DN_ZMem z_mem); -DN_API void DN_OS_MemDealloc (void *ptr); +DN_API void * DN_OS_MemAlloc (DN_USize count, DN_ZMem z_mem); +DN_API void DN_OS_MemDealloc (void *ptr); -DN_API DN_Date DN_OS_DateLocalTimeNow (); -DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now (char date_seperator = '-', char hms_seperator = ':'); -DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8 (DN_Date time, char date_seperator = '-', char hms_seperator = ':'); -DN_API DN_U64 DN_OS_DateUnixTimeNs (); -#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000) -#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000)) -#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000)) -DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate (DN_Date date); -DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS (DN_U64 unix_ts_s); +DN_API DN_Date DN_OS_DateLocalTimeNow (); +DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now (char date_seperator = '-', char hms_seperator = ':'); +DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8 (DN_Date time, char date_seperator = '-', char hms_seperator = ':'); +DN_API DN_U64 DN_OS_DateUnixTimeNs (); +#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000) +#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000)) +#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000)) +DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate (DN_Date date); +DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS (DN_U64 unix_ts_s); -DN_API void DN_OS_GenBytesSecure (void *buffer, DN_U32 size); -DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value); -DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path); -DN_API DN_Str8 DN_OS_ExePath (DN_Arena *arena); -DN_API DN_Str8 DN_OS_ExeDir (DN_Arena *arena); -DN_API void DN_OS_OpenUrl (DN_Str8 url); -DN_API void DN_OS_SleepMs (DN_UInt milliseconds); +DN_API void DN_OS_GenBytesSecure (void *buffer, DN_U32 size); +DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value); +DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path); +DN_API DN_Str8 DN_OS_ExePath (DN_Arena *arena); +DN_API DN_Str8 DN_OS_ExeDir (DN_Arena *arena); +DN_API void DN_OS_OpenUrl (DN_Str8 url); +DN_API void DN_OS_SleepMs (DN_UInt milliseconds); -DN_API DN_U64 DN_OS_PerfCounterNow (); -DN_API DN_U64 DN_OS_PerfCounterFrequency (); -DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end); -DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end); -DN_API DN_OSTimer DN_OS_TimerBegin (); -DN_API void DN_OS_TimerEnd (DN_OSTimer *timer); -DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer); -DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer); -DN_API DN_U64 DN_OS_EstimateTscPerSecond (uint64_t duration_ms_to_gauge_tsc_frequency); +DN_API DN_U64 DN_OS_PerfCounterNow (); +DN_API DN_U64 DN_OS_PerfCounterFrequency (); +DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end); +DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end); +DN_API DN_OSTimer DN_OS_TimerBegin (); +DN_API void DN_OS_TimerEnd (DN_OSTimer *timer); +DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer); +DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer); +DN_API DN_U64 DN_OS_EstimateTscPerSecond (uint64_t duration_ms_to_gauge_tsc_frequency); // NOTE: OS File // Overview @@ -4793,6 +5081,7 @@ DN_API void DN_OS_WindowMinimise (D DN_API void DN_OS_WindowBringToFront (DN_OSWindow *window); DN_API void DN_OS_WindowFocus (DN_OSWindow *window); DN_API bool DN_OS_WindowIsFocused (DN_OSWindow *window); +DN_API void DN_OS_WindowCustomTitlebarSet (DN_OSWindow *window, bool custom_titlebar); // NOTE: Thread // Overview diff --git a/build.bat b/build.bat index d36fb66..cf6e021 100644 --- a/build.bat +++ b/build.bat @@ -34,17 +34,18 @@ pushd %build_dir% :: GR- Disable C RTTI :: Oi Use CPU Intrinsics :: Z7 Combine multi-debug files to one debug file + set flags= set flags=%flags% %script_dir%\Source\dn_tests.cpp set msvc_driver_flags=-EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo %flags% -fsanitize=address where /q emcc && ( echo [BUILD] Emscripten emcc detected, compiling ... - call emcc -g -msimd128 -msse2 %flags% -o %build_dir%\dn_tests_emcc.js -s FETCH=1 -pthread -s ASYNCIFY=1 -lwebsocket -Wall || echo EMCC build failed&& exit /b 1 + call emcc -g -msimd128 -msse2 !flags! -o %build_dir%\dn_tests_emcc.js -s FETCH=1 -pthread -s ASYNCIFY=1 -lwebsocket -Wall || echo EMCC build failed&& exit /b 1 ) where /q cl && ( echo [BUILD] MSVC cl detected, compiling ... - set msvc_cmd=cl -MTd %msvc_driver_flags% -analyze -Fe:dn_tests_msvc -Fo:dn_tests_msvc + set msvc_cmd=cl -MTd !msvc_driver_flags! -analyze -Fe:dn_tests_msvc -Fo:dn_tests_msvc if exist %build_dir%/Curl/Install/lib/libcurl-d.lib ( set msvc_cmd=!msvc_cmd! -D DN_WITH_NET_CURL=1 -I %build_dir%/Curl/Install/include %build_dir%/Curl/Install/lib/libcurl-d.lib crypt32.lib ws2_32.lib advapi32.lib wldap32.lib iphlpapi.lib secur32.lib ) @@ -52,20 +53,21 @@ pushd %build_dir% REM Build the single header powershell -Command "$time = Measure-Command { !msvc_cmd! | Out-Default }; Write-Host '[BUILD] msvc:'$time.TotalSeconds's'; exit $LASTEXITCODE" || echo MSVC build failed&& exit /b 1 - echo [BUILD] Single header generator ... + echo [BUILD] Single header generator: USE_SINGLE_HEADER not defined... call cl %script_dir%\single_header_generator.cpp -Z7 -nologo -link || echo Single header generator build failed&& exit /b 1 call %build_dir%\single_header_generator.exe %script_dir%\Source %script_dir%\Single-Header || echo Single header generation failed&& exit /b 1 REM Build the single header using the single header (to test that the generated single header) + echo [BUILD] Single header generator: USE_SINGLE_HEADER defined... call cl %script_dir%\single_header_generator.cpp -Z7 -nologo -D USE_SINGLE_HEADER -link || echo Single header generator build failed&& exit /b 1 call %build_dir%\single_header_generator.exe %script_dir%\Source %script_dir%\Single-Header || echo Single header generation failed&& exit /b 1 ) where /q clang-cl && ( echo [BUILD] LLVM clang-cl detected, compiling ... - set clang_cmd=clang-cl -MT %msvc_driver_flags% -fsanitize=undefined -Fe:dn_tests_clang -link + set clang_cmd=clang-cl -MT !msvc_driver_flags! -fsanitize=undefined -Fe:dn_tests_clang -link powershell -Command "$time = Measure-Command { !clang_cmd! | Out-Default }; Write-Host '[BUILD] clang-cl:'$time.TotalSeconds's'; exit $LASTEXITCODE" || echo CLANG build failed&& exit /b 1 ) - exit /b 1 -popd + popd + exit /b 0