From 544669d3cba80d3070fe7b32c852ee8145d4968c Mon Sep 17 00:00:00 2001 From: doylet Date: Tue, 23 Jun 2026 21:13:06 +1000 Subject: [PATCH] Get latest changes from Primitive Indexer --- Single-Header/dn_single_header.cpp | 213 ++++++++++------ Single-Header/dn_single_header.h | 379 +++++++++++++++++++---------- Source/Base/dn_base.cpp | 166 +++++++++---- Source/Base/dn_base.h | 289 ++++++++++++++-------- Source/Extra/dn_net_curl.cpp | 6 +- Source/Extra/dn_net_emscripten.cpp | 5 +- Source/OS/dn_os_posix.cpp | 35 ++- Source/OS/dn_os_w32.cpp | 10 +- Source/dn.h | 88 +++++-- build.bat | 5 + single_header_generator.cpp | 6 +- 11 files changed, 786 insertions(+), 416 deletions(-) diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index c188b6c..548da7d 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-06-18 22:11:00 +// Generated by the DN single header generator 2026-06-23 21:12:54 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_H_WITH_OS 1 @@ -24,6 +24,54 @@ enum DN_ArenaUAFCheckReportType_ DN_ArenaUAFCheckReportType_TempEndOutOfOrder, }; +DN_API bool DN_VerifyArgsF(DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8, char const *fmt, ...) +{ + bool result = expr; + if (result) + return result; + + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); + { + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + + // NOTE: Log message prefix + DN_Str8BuilderAppendF(&builder, "Verify [%.*s] failed%s", DN_Str8PrintFmt(expr_str8), fmt ? ". " : ""); + + // NOTE: Log user message + if (fmt) { + va_list args; + va_start(args, fmt); + DN_Str8BuilderAppendFV(&builder, fmt, args); + va_end(args); + } + + // NOTE: Log stack trace + if (DN_PARANOIA_LEVEL) { + if (type == DN_VerifyType_Nil) { + DN_Str8 trace = DN_Str8FromStackTraceNowArena(&scratch.arena, 128 /*limit*/, 4 /*skip*/); + DN_Str8BuilderAppendF(&builder, "\nTrace:\n "); + DN_Str8BuilderAppendRef(&builder, DN_Str8PadNewLinesArena(trace, DN_Str8Lit(" "), &scratch.arena)); + } + } + + DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, &scratch.arena); + DN_LogType log_type = type == DN_VerifyType_Nil ? DN_LogType_Error : DN_LogType_Warning; + DN_LogTypeParam log_type_param = DN_LogTypeParamFromType(log_type); + DN_LogPrintF(log_type_param, call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); + } + DN_TCScratchEnd(&scratch); + + if (type == DN_VerifyType_Nil && DN_PARANOIA_LEVEL) { + DN_DebugBreak; + } + return result; +} + +DN_API bool DN_VerifyArgs(DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8) { + bool result = DN_VerifyArgsF(type, expr, call_site, expr_str8, /*fmt=*/ 0); + return result; +} + DN_API bool DN_MemStartsWith(void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size) { bool result = false; @@ -167,7 +215,7 @@ DN_API DN_CPUReport DN_CPUGetReport() int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000; int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax); - // NOTE: Query standard/extended numbers /////////////////////////////////////////////////////// + // NOTE: Query standard/extended numbers { DN_CPUIDArgs args = {}; @@ -181,11 +229,11 @@ DN_API DN_CPUReport DN_CPUGetReport() fn_8000_[0] = DN_CPUID(args); } - // NOTE: Extract function count //////////////////////////////////////////////////////////////// + // NOTE: Extract function count int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax; int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax; - // NOTE: Enumerate all CPUID results for the known function counts ///////////////////////////// + // NOTE: Enumerate all CPUID results for the known function counts { DN_AssertF((STANDARD_FUNC_MAX_EAX + 1) <= DN_ArrayCountI(fn_0000_), "Max standard count is %d", @@ -207,14 +255,14 @@ DN_API DN_CPUReport DN_CPUGetReport() } } - // NOTE: Query CPU vendor ////////////////////////////////////////////////////////////////////// + // NOTE: Query CPU vendor { DN_Memcpy(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE); DN_Memcpy(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE); DN_Memcpy(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE); } - // NOTE: Query CPU brand /////////////////////////////////////////////////////////////////////// + // NOTE: Query CPU brand if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) { DN_Memcpy(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE); DN_Memcpy(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE); @@ -234,7 +282,7 @@ DN_API DN_CPUReport DN_CPUGetReport() DN_Assert(result.brand[sizeof(result.brand) - 1] == 0); } - // NOTE: Query CPU features ////////////////////////////////////////////////////////////////// + // NOTE: Query CPU features for (DN_USize ext_index = 0; ext_index < DN_CPUFeature_Count; ext_index++) { bool available = false; @@ -296,7 +344,7 @@ DN_API DN_CPUReport DN_CPUGetReport() case DN_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break; case DN_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break; case DN_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break; - case DN_CPUFeature_Count: DN_InvalidCodePath; break; + case DN_CPUFeature_Count: DN_AssertInvalidCodePath; break; } if (available) @@ -306,7 +354,6 @@ DN_API DN_CPUReport DN_CPUGetReport() return result; } -// NOTE: DN_TicketMutex //////////////////////////////////////////////////////////////////////////// DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex) { DN_UInt ticket = DN_AtomicAddU32(&mutex->ticket, 1); @@ -349,7 +396,7 @@ DN_API bool DN_TicketMutex_CanLock(DN_TicketMutex const *mutex, DN_UInt ticket) #endif #endif -// NOTE: DN_Bit //////////////////////////////////////////////////////////////////////////////////// +// NOTE: DN_Bit DN_API void DN_BitUnsetInplace(DN_USize *flags, DN_USize bitfield) { *flags = (*flags & ~bitfield); @@ -362,7 +409,13 @@ DN_API void DN_BitSetInplace(DN_USize *flags, DN_USize bitfield) DN_API bool DN_BitIsSet(DN_USize bits, DN_USize bits_to_set) { - auto result = DN_Cast(bool)((bits & bits_to_set) == bits_to_set); + bool result = DN_Cast(bool)((bits & bits_to_set) == bits_to_set); + return result; +} + +DN_API bool DN_BitIsAny(DN_USize bits, DN_USize bits_to_check) +{ + bool result = DN_Cast(bool)(bits & bits_to_check); return result; } @@ -788,8 +841,8 @@ DN_API void DN_ASanPoisonMemoryRegion(void const volatile *ptr, DN_USize size) __asan_poison_memory_region(ptr, size); if (DN_ASAN_VET_POISON) { - DN_HardAssert(__asan_address_is_poisoned(ptr)); - DN_HardAssert(__asan_address_is_poisoned((char *)ptr + (size - 1))); + DN_AssertAlways(__asan_address_is_poisoned(ptr)); + DN_AssertAlways(__asan_address_is_poisoned((char *)ptr + (size - 1))); } #else (void)ptr; @@ -805,7 +858,7 @@ DN_API void DN_ASanUnpoisonMemoryRegion(void const volatile *ptr, DN_USize size) #if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) __asan_unpoison_memory_region(ptr, size); if (DN_ASAN_VET_POISON) - DN_HardAssert(__asan_region_is_poisoned((void *)ptr, size) == 0); + DN_AssertAlways(__asan_region_is_poisoned((void *)ptr, size) == 0); #else (void)ptr; (void)size; @@ -881,8 +934,8 @@ static bool DN_ArenaHasPoison_(DN_MemFlags flags) static DN_MemBlock *DN_MemBlockFromMemFuncsFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_MemFuncs mem_funcs) { - bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; - bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; + bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; + bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; DN_MemBlock *result = DN_ArenaBlockFromMemFuncs_(reserve, commit, track_alloc, alloc_can_leak, mem_funcs); if (result && DN_ArenaHasPoison_(flags)) DN_ASanPoisonMemoryRegion(DN_Cast(char *) result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE); @@ -1062,21 +1115,21 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem return nullptr; try_alloc_again: - DN_MemBlock *curr = mem->curr; - bool poison = DN_ArenaHasPoison_(mem->flags); - DN_U8 real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; - DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0); - DN_U64 end_pos = offset_pos + size; - DN_U64 alloc_size = end_pos - curr->used; + DN_MemBlock *curr = mem->curr; + bool poison = DN_ArenaHasPoison_(mem->flags); + DN_U8 real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; + DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0); + DN_U64 end_pos = offset_pos + size; + DN_U64 alloc_size = end_pos - curr->used; - if (end_pos > curr->reserve) { - if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) - return nullptr; - DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE); - DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE); - if (!DN_MemListGrow(mem, new_reserve, new_commit)) - return nullptr; - goto try_alloc_again; + if (end_pos > curr->reserve) { + if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) + return nullptr; + DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE); + DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE); + if (!DN_MemListGrow(mem, new_reserve, new_commit)) + return nullptr; + goto try_alloc_again; } DN_USize prev_arena_commit = curr->commit; @@ -1096,8 +1149,8 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem mem->stats.hwm.commit = DN_Max(mem->stats.hwm.commit, mem->stats.info.commit); } - void *result = DN_Cast(char *) curr + offset_pos; - curr->used += alloc_size; + void *result = DN_Cast(char *) curr + offset_pos; + curr->used += alloc_size; mem->stats.info.used += alloc_size; mem->stats.hwm.used = DN_Max(mem->stats.hwm.used, mem->stats.info.used); @@ -1167,7 +1220,19 @@ DN_API void DN_MemListPopTo(DN_MemList *mem, DN_U64 init_used) if (DN_SCRUB_UNINIT_MEM_BYTE) { if (old_used > curr->used) { char *discarded = (char *)curr + curr->used; - DN_Memset(discarded, DN_SCRUB_UNINIT_MEM_BYTE, old_used - curr->used); + DN_USize scrub_size = old_used - curr->used; + + // NOTE: If we allocated memory unaligned then the pointer given to the user was aligned up + // and unpoisoned. If the user snapped a memory list position before that allocation then + // attempts to revert it, scrubbing from the memory position (which is before alignment was + // applied!) will cause this code to accidentally scrub the our poison guard bytes. So we + // unpoison the region unconditionally to ensure that is cleaned up before scrubbing. Since + // scrubbing is a debug feature and, you have it turned on _with_ ASAN then we let that + // performance penalty slide. + if (DN_ArenaHasPoison_(mem->flags)) + DN_ASanUnpoisonMemoryRegion(discarded, scrub_size); + + DN_Memset(discarded, DN_SCRUB_UNINIT_MEM_BYTE, scrub_size); } } DN_MSVC_WARNING_POP @@ -1318,7 +1383,7 @@ static void DN_ArenaUAFCheck_(DN_Arena *arena, DN_ArenaUAFCheckReportType_ type) DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" - "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", + "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s\n", DN_Str8PrintFmt(prefix), arena->uaf_guard_id, DN_Str8PrintFmt(curr_stack_trace), @@ -1705,7 +1770,7 @@ DN_API bool DN_ErrSinkEndLogError_(DN_ErrSink *err, DN_CallSite call_site, DN_St // NOTE: Log the error DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, err->arena); - DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); + DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); if (node->mode == DN_ErrSinkMode_DebugBreakOnErrorLog) DN_DebugBreak; @@ -1835,7 +1900,7 @@ DN_API void DN_TCEquip(DN_TCCore *tc) DN_API DN_TCCore *DN_TCGet() { - DN_RawAssert(g_dn_thread_context && + DN_AssertRaw(g_dn_thread_context && "This thread's thread context has not been equipped yet. Ensure that DN_TCInit(...) " "has been called to create a thread context and call DN_TCEquip(...) in the current " "thread to make it retrievable via this function"); @@ -5805,22 +5870,25 @@ DN_API void DN_LogSetPrintFunc(DN_LogPrintFunc *print_func, void *user_data) dn->print_func_context = user_data; } -DN_API void DN_LogPrint(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...) +DN_API void DN_LogPrintFV(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args) { DN_Core *dn = DN_Get(); if (type.is_u32_enum) { DN_Assert(dn->log_level_to_show_from >= 0); - if (type.u32 < DN_Cast(DN_U32)dn->log_level_to_show_from) + if (type.u32 < DN_Cast(DN_U32) dn->log_level_to_show_from) return; } - DN_LogPrintFunc *func = dn->print_func; - if (func) { - va_list args; - va_start(args, fmt); + if (func) func(type, dn->print_func_context, call_site, flags, fmt, args); - va_end(args); - } +} + +DN_API void DN_LogPrintF(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + DN_LogPrintFV(type, call_site, flags, fmt, args); + va_end(args); } DN_API DN_LogTypeParam DN_LogTypeParamFromType(DN_LogType type) @@ -7751,7 +7819,7 @@ DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USiz DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site) { void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + DN_AssertCallSiteF(result, call_site, "Array out of space, failed to add %zu items: array=%p size=%zu max=%zu", make_count, data, *size, max); return result; } @@ -7774,7 +7842,7 @@ DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) { void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + DN_AssertCallSiteF(result, call_site, "Array out of space, failed to add %zu items: array=%p size=%zu max=%zu", elems_count, data, *size, max); return result; } @@ -8010,7 +8078,7 @@ DN_U32 DN_DSMapHash(DN_DSMap const *map, DN_DSMapKey key) DN_U32 h = seed; switch (key.type) { case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ - case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/ + case DN_DSMapKeyType_U64NoHash: DN_AssertInvalidCodePath; /*FALLTHRU*/ case DN_DSMapKeyType_Invalid: break; case DN_DSMapKeyType_Buffer: @@ -8902,7 +8970,7 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); - DN_HardAssertF( + DN_AssertAlwaysF( alloc->flags & DN_LeakAllocFlag_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 " @@ -8950,7 +9018,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); - DN_HardAssertF(alloc_entry.found, + DN_AssertAlwaysF(alloc_entry.found, "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]", @@ -8959,7 +9027,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_LeakAlloc *alloc = alloc_entry.value; if (alloc->flags & DN_LeakAllocFlag_Freed) { DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); - DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, + DN_AssertAlwaysF((alloc->flags & DN_LeakAllocFlag_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" @@ -10470,7 +10538,7 @@ static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect) DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc"); #endif unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags); @@ -10489,7 +10557,7 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_fl DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif bool result = false; if (!ptr || size == 0) @@ -10505,7 +10573,7 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags) DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif mprotect(ptr, size, PROT_NONE); madvise(ptr, size, MADV_FREE); @@ -10514,7 +10582,7 @@ DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size) DN_API void DN_OS_MemRelease(void *ptr, DN_USize size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif munmap(ptr, size); } @@ -10522,7 +10590,7 @@ DN_API void DN_OS_MemRelease(void *ptr, DN_USize size) DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif if (!ptr || size == 0) return 0; @@ -10638,7 +10706,7 @@ DN_API DN_Date DN_OS_DateUnixTimeSToDate(DN_U64 time) DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePath; + DN_AssertInvalidCodePath; (void)buffer; (void)size; #else @@ -10658,7 +10726,7 @@ DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value) { - DN_AssertFOnce(false, "Unimplemented"); + DN_VerifyWarning(false, "Unimplemented function"); (void)name; (void)value; bool result = false; @@ -10884,18 +10952,14 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, return result; if ((access & ~(DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0))) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; return result; } if (access & DN_OSFileAccess_Execute) { result.error = true; - DN_ErrSinkAppendF( - error, - 1, - "Failed to open file '%.*s': File access flag 'execute' is not supported", - DN_Str8PrintFmt(path)); - DN_InvalidCodePath; // TODO: Not supported via fopen + DN_ErrSinkAppendF(error, 1, "Failed to open file '%.*s': File access flag 'execute' is not supported", DN_Str8PrintFmt(path)); + DN_AssertInvalidCodePath; // TODO: Not supported via fopen return result; } @@ -10909,7 +10973,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, case DN_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break; case DN_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break; case DN_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break; - default: DN_InvalidCodePath; break; + default: DN_AssertInvalidCodePath; break; } if (!handle) { // TODO(doyle): FileOpen flag to string @@ -11186,7 +11250,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, } #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Unsupported operation"); + DN_AssertInvalidCodePathF("Unsupported operation"); #endif static_assert(sizeof(pid_t) <= sizeof(handle.process), @@ -11273,10 +11337,9 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_ErrSink *error) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Unsupported operation"); + DN_AssertInvalidCodePathF("Unsupported operation"); #endif - DN_AssertFOnce(args->environment.count == 0, "Unimplemented in POSIX"); - + DN_VerifyWarningF(args->environment.count == 0, "Environment variables are unimplemented in POSIX"); DN_OSExecAsyncHandle result = {}; if (cmd_line.count == 0) return result; @@ -11464,7 +11527,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, DN_U32 timeout_ms, DN_ErrSink *err) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; DN_OSExecResult result = {}; return result; } @@ -11987,7 +12050,7 @@ DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags) DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem) { DN_Core *dn = DN_Get(); - DN_RawAssert(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag"); + DN_AssertRaw(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag"); DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0; DN_Assert(size <= DN_Cast(DWORD)(-1)); void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size); @@ -12227,7 +12290,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile return result; if ((access & ~DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0)) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; return result; } @@ -12235,8 +12298,8 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile switch (open_mode) { case DN_OSFileOpen_CreateAlways: create_flag = CREATE_ALWAYS; break; case DN_OSFileOpen_OpenIfExist: create_flag = OPEN_EXISTING; break; - case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break; - default: DN_InvalidCodePath; return result; + case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break; + default: DN_AssertInvalidCodePath; return result; } unsigned long access_mode = 0; @@ -12386,7 +12449,7 @@ DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path) } result.exists = true; -result.create_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreationTime); + result.create_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreationTime); result.last_access_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastAccessTime); result.last_write_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastWriteTime); diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index a8258e2..059dd6c 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -1,10 +1,11 @@ -// Generated by the DN single header generator 2026-06-18 22:11:00 +// Generated by the DN single header generator 2026-06-23 21:12:54 #if !defined(DN_H) #define DN_H // NOTE: DN -// Getting Started + +// NOTE: Getting Started // Include this mega header `dn.h` and define the following symbols to `1` to conditionally // enable the interfaces for those features. Additionally in the same or different translation // unit, include `dn.cpp` with the same symbols defined to enable the implementation of these @@ -35,9 +36,10 @@ // synchronisation, memory allocation. This layer is OPTIONAL. // // - Extra layer provides helper utilities that are opt-in. These layers are OPTIONAL. -// -// Configuration -// Platform Target + +// NOTE: Configuration + +// NOTE: Platform Target // Define one of the following directives to configure this library to compile for that // platform. By default, the library will auto-detect the current host platform and select that // as the target platform. @@ -52,8 +54,8 @@ // // Will ensure that is included and the OS layer is implemented using Win32 // primitives. -// -// Static functions + +// NOTE: Static functions // All public functions in the DN library are prefixed with the macro '#define DN_API'. By // default 'DN_API' is not defined to anything. Define // @@ -62,8 +64,8 @@ // To replace all the functions prefixed with DN_API to be prefixed with 'static' ensuring that // the functions in the library do not export an entry into the linking table. // translation units. -// -// Disabling the in-built (if #define DN_H_WITH_OS 1) + +// NOTE: Disabling the in-built (if #define DN_H_WITH_OS 1) // If you are building DN for the Windows platform, is a large legacy header that // applications have to include to use Windows APIs. By default this library uses a replacement // header for all the Windows functions that it uses in the OS layer removing the need to @@ -76,16 +78,16 @@ // To instead use . DN automatically detects if is included in an earlier // translation unit and will automatically disable the in-built replacement header in which case // this does not need to be defined. -// -// Freestanding + +// NOTE: Freestanding // The base layer can be used without an OS implementation by defining DN_FREESTANDING like: // // #define DN_FREESTANDING // // This means functionality that relies on the OS like printing, memory allocation, stack traces // and so forth are disabled. -// -// ASAN Arena Poisoning + +// NOTE: ASAN Arena Poisoning // When compiled with address sanitizer (.e.g -fsanitize=address) you can optionally enable // memory region poisoning on the inbuilt arena's to catch in certain scenarios, use-after-free // @@ -95,8 +97,8 @@ // memory markup that ASAN does and so it is implemented manually by using the ASAN user-level // poisoning APIs. Similarly, since the arena recycles its own memory rather than release back // to the OS, poisoning is not as effective for arenas but every little bit helps. -// -// Scrub Uninitialised Memory + +// NOTE: Scrub Uninitialised Memory // If this macro is defined, temp memory that is returned to an arena, or allocations freed by // a pool are scrubbed to this specified byte, in absence of this bytes returned to the // allocators are left as-is or memset to 0. For example to scrub bytes to 0xCD (MSVC's @@ -107,8 +109,8 @@ // Due to the recycling of memory in arenas and pool, similarly to ASAN poisoning this reduces // the window in which a use-after-free can be detected using this guard, however every little // bit helps. -// -// Arena temp memory use-after-free (UAF) tooling + +// NOTE: Arena temp memory use-after-free (UAF) tooling // UAF Guard // Set the following preprocessor value to 1 to enable UAF protection when using // scratch/temporary memory functionality. Defaults to off, or 0 if not specified @@ -129,7 +131,7 @@ // memory block and additional book-keeping fields on each arena and their temp memory // instances. // -// UAF Tracing +// NOTE: UAF Tracing // Set the following preprocessor value to 1 to enable tracing when the UAF guard triggers. // Defaults to off, or 0 if not specified. // @@ -143,11 +145,57 @@ // Tracing incurs an additional much heavier performance penalty than the UAF guard due to // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. + +// NOTE: 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. Defaults to +// paranoia level 0 in release builds and level 1 for debug. // -// Str8 AVX512F variants -// We have some AVX512 string functions that can be enabled by defining the following +// #define DN_PARANOIA_LEVEL 1 // -// #define DN_STR8_AVX512F 1 +// Each level activates the following debug mechanisms. Note that any of the following #defines +// enabled by a paranoia level can be overridden by defining the preprocessor definition before +// the inclusion of this file. +// +// Level 0 +// `DN_Assert` calls are compiled out +// +// `DN_Verify` calls logs an error and continues +// +// `DN_VerifyWarning` calls logs a warning and continues +// +// Level 1 +// `DN_Assert` calls are compiled in +// +// `DN_Verify` calls a debug trap rather than just logging and continuing +// +// `DN_Verify` calls dump a stack trace when triggered +// +// `DN_ASAN_POISON` is set. When an arena allocates memory unallocated bytes from the +// memory owned by the arena are manually poisoned using ASAN. A fault will be triggered if +// the memory is written to (UAF e.g. use-after-free). Address sanitizer must be enabled or +// otherwise this is a no-op. This incurs a performance penalty on-top of the overhead of +// running ASAN on your binary as recycling memory calls into ASAN to poison the region. +// +// `DN_ARENA_TEMP_MEM_UAF_GUARD` is set. When an arena uses temporary memory it will record +// the active temporary memory region and compare them when allocating to ensure that +// memory is allocated in the active region otherwise a UAF fault is triggered. This has a +// small runtime performance penalty. +// +// `DN_SCRUB_UNINIT_MEM_BYTE` is set to `0xCD`. When memory is cleared in an arena or a +// pool backed by an arena upon deallocation if the `DN_ZMem_Yes` flag is passed then the +// bytes are scrubbed to this byte to make UAF more salient. +// +// Level 2 +// `DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT` is set. When an arena uses temporary memory +// regions that region's a stack trace of the call site is recorded. This is very expensive +// but when a temporary memory region is used after it has been deallocated, a full stack +// trace diagnostic is available of where the various regions where created and freed. +// +// NOTE: Str8 AVX512F variants +// We have some AVX512 string functions that can be enabled by defining the following +// +// #define DN_STR8_AVX512F 1 // DN: Single header generator commented out => #include "Base/dn_base.h" #if !defined(DN_BASE_H) @@ -155,6 +203,7 @@ // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_STR8_AVX512F 1 +// #define DN_PARANOIA_LEVEL 1 // #include "../dn.h" // #endif @@ -299,99 +348,160 @@ #define DN_ZeroInit {0} #endif -// NOTE: Address sanitizer -#if !defined(DN_ASAN_POISON) - #define DN_ASAN_POISON 0 -#endif +// NOTE: Macros +#define DN_Stringify(x) #x +#define DN_TokenCombine2(x, y) x ## y +#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) -#if !defined(DN_ASAN_VET_POISON) - #define DN_ASAN_VET_POISON 0 -#endif +// NOTE: Error Checking/Validating +// Asserts are useful to verify invariants in the codebase, but there's sometimes the ambiguous +// question of what should be asserted, what happens when we should have triggered an assert +// in a release build (where they are canonically turned off), what alternative mechanisms should we +// use for error checking that should be visible to non-developers. +// +// The following is an excerpt from Tom Forsyth's assertion article which he references Chris +// Hargrove's guidelines on how asserts show be used. It is quite reasonable and we model our +// primitives after based on those concepts: +// +// Logging, asserts and unit tests (https://tomforsyth1000.github.io/blog.wiki.html +// +// Assert: Immediately fatal, and not ignorable. Fundamental assumption by an engineer has been +// disproven and needs immediate handling. Requires discipline on the part of the engineer to not +// add them in situations that are actually non-fatal (rule of thumb being that if a crash would +// be almost certain to happen anyway due to the same condition, then you’re no worse off making +// an assert). +// +// Errors: Probably fatal soon, but not necessarily immediately. Basically a marker for “you are +// now in a f*cked state, you might limp along a bit, but assume nothing”. Game continues, but an +// ugly red number gets displayed onscreen for how many of these have been encountered (so when +// people send you screenshots of bugs you can then point to the red error count and blame +// accordingly). Savegames are disabled from this point so as not to make the error effectively +// permanent; you should also deliberately violate a few other TCRs as soon as an error is +// encountered in order to ensure that all parties up and down the publisher/developer chain are +// aware of how bad things are. Errors are technically “ignorable” but everyone knows that it +// might only buy you a little bit of borrowed time; these are only a small step away from the +// immediately-blocking nature of an assert, but sometimes that small step can have a big impact +// on productivity. +// +// Warnings: Used for “you did something bad, but we caught it so it’s fine (the game state is +// still okay), however it might not be fine in the future so if you want to save yourself some +// headache you should fix this sooner rather than later”. Great for content problems. Also +// displayed onscreen as a yellow number (near the red error number). You can keep these around +// for a while and triage them when their utility is called into question. +// +// Crumbs: The meta-category for a large number of “verbose” informational breadcrumb categories +// that must be explicitly enabled so you don’t clutter everything up and obscure stuff that +// matters. Note that the occurrance of certain Errors should automatically enable relevant +// categories of crumbs so that more detailed information about the aforementioned f*cked state +// will be provided during the limp-along timeframe. +// +// In the excerpt, their domain (games programming) prioritises continuity over immediate failure +// on warning and error as this allows non-developer clientele to continue using the application +// despite error laden states. This is useful in general as not all failures are critical to the +// use case that the end user is dealing with. +// +// We model `Errors` and `Warnings` as `DN_Verify` and `DN_VerifyWarning` respectively. The verify +// variants check the expression to test, log and a message and allow the developer to branch on the +// result and "recover" where appropriate. Verify checks are never compiled out. We have traditional +// `Asserts` as `DN_Assert` which can be compiled out. +// +// The article also defines what it calls a paranoia level. We `#define DN_PARANOIA_LEVEL ` +// to customise the validation layers of the codebase. See DN_PARANOIA_LEVEL in the customisation +// section for more information. +// +// In summary use each of the primitives in these situation: +// +// `DN_Assert`: Fatal and immediately needs attention and can be compiled out +// +// `DN_Verify`: Fatal or eventually fatal but not necessarily immediately, program is or will +// degenerate into an incorrect state. Is always compiled in and is visible in non-debug +// environments. +// +// `DN_VerifyWarning`: Something bad happened, but we caught it and recovered from it. Program +// state remains consistent. It is always compiled in and is visible in non-debug environments. -#define DN_ASAN_POISON_ALIGNMENT 8 - -#if !defined(DN_ASAN_POISON_GUARD_SIZE) - #define DN_ASAN_POISON_GUARD_SIZE 128 +#if !defined(DN_PARANOIA_LEVEL) + #if defined(NDEBUG) + #define DN_PARANOIA_LEVEL 0 + #else + #define DN_PARANOIA_LEVEL 1 + #endif #endif #if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) #include #endif -// NOTE: Memory +#define DN_ASAN_POISON_ALIGNMENT 8 +#if !defined(DN_ASAN_VET_POISON) + #define DN_ASAN_VET_POISON 0 +#endif + +#if !defined(DN_ASAN_POISON) + #if DN_PARANOIA_LEVEL >= 1 + #define DN_ASAN_POISON 1 + #else + #define DN_ASAN_POISON 0 + #endif +#endif + +#if !defined(DN_ASAN_POISON_GUARD_SIZE) + #define DN_ASAN_POISON_GUARD_SIZE 128 +#endif + #if !defined(DN_ARENA_TEMP_MEM_UAF_GUARD) - #define DN_ARENA_TEMP_MEM_UAF_GUARD 0 + #if DN_PARANOIA_LEVEL >= 1 + #define DN_ARENA_TEMP_MEM_UAF_GUARD 1 + #else + #define DN_ARENA_TEMP_MEM_UAF_GUARD 0 + #endif #endif #if !defined(DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT) - #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0 + #if DN_PARANOIA_LEVEL >= 2 + #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 1 + #else + #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0 + #endif #endif #if !defined(DN_SCRUB_UNINIT_MEM_BYTE) - #define DN_SCRUB_UNINIT_MEM_BYTE 0 + #if DN_PARANOIA_LEVEL >= 1 + #define DN_SCRUB_UNINIT_MEM_BYTE 0xCD + #else + #define DN_SCRUB_UNINIT_MEM_BYTE 0x00 + #endif #endif -// NOTE: Macros -#define DN_Stringify(x) #x -#define DN_TokenCombine2(x, y) x ## y -#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) - -// NOTE: Asserts -#define DN_HardAssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ - DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ +#define DN_AssertRaw(expr) do { if (!(expr)) DN_DebugBreak; } while (0) +#define DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogTypeParam log_type_ = DN_LogTypeParamFromType(DN_LogType_Error); \ + DN_LogPrintF(log_type_, call_site, DN_LogFlags_Nil, "Assertion triggered [" #expr "]. " fmt "\nTrace:\n%.*s", ## __VA_ARGS__, DN_Str8PrintFmt(trace_)); \ + DN_DebugBreak; \ + } \ } while (0) -#define DN_HardAssert(expr) DN_HardAssertF(expr, "") -// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if -// we're trying to detect some code related to the DN startup sequence. If we try to assert before -// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to -// be setup which belongs to the OS. -// -// This causes recursion errors as they call into each other. We use RawAsserts for these kind of -// checks. -#if defined(DN_NO_ASSERT) - #define DN_RawAssert(...) - #define DN_Assert(...) - #define DN_AssertOnce(...) - #define DN_AssertArgsF(...) - #define DN_AssertF(...) - #define DN_AssertFOnce(...) +#define DN_AssertAlwaysF(expr, fmt, ...) DN_AssertAlwaysCallSiteF(expr, (DN_CallSiteNow), fmt, ##__VA_ARGS__) +#define DN_AssertAlways(expr) DN_AssertAlwaysF(expr, "") +#define DN_AssertInvalidCodePathF(fmt, ...) DN_AssertAlwaysF(0, fmt, ##__VA_ARGS__) +#define DN_AssertInvalidCodePath DN_AssertInvalidCodePathF("Invalid code path") +#if DN_PARANOIA_LEVEL >= 1 +#define DN_AssertCallSiteF(expr, call_site, fmt, ...) DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ## __VA_ARGS__) +#define DN_AssertF(expr, fmt, ...) DN_AssertCallSiteF(expr, (DN_CallSiteNow), fmt, ## __VA_ARGS__) +#define DN_Assert(expr) DN_AssertAlways(expr) #else - #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) - #define DN_AssertArgsF(expr, call_site, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ - DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), \ - call_site, \ - DN_LogFlags_Nil, \ - "Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_AssertF(expr, fmt, ...) DN_AssertArgsF(expr, DN_CALL_SITE, fmt, ## __VA_ARGS__) - #define DN_AssertFOnce(expr, fmt, ...) \ - do { \ - for (static bool once_ = true; !(expr) && once_; once_ = false) \ - DN_AssertF(expr, fmt, ## __VA_ARGS__); \ - } while (0) - - #define DN_Assert(expr) DN_AssertF((expr), "") - #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") +#define DN_AssertCallSiteF(expr, call_site, fmt, ...) (void)(expr); (void)call_site +#define DN_AssertF(expr, fmt, ...) (void)(expr) +#define DN_Assert(expr) (void)(expr) #endif +#define DN_VerifyF(expr, fmt, ...) DN_VerifyArgsF(DN_VerifyType_Nil, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__) +#define DN_VerifyWarningF(expr, fmt, ...) DN_VerifyArgsF(DN_VerifyType_Warning, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__) +#define DN_Verify(expr) DN_VerifyF(expr, 0) +#define DN_VerifyWarning(expr) DN_VerifyWarningF(expr, 0) -#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) -#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") #define DN_StaticAssert(expr) \ DN_GCC_WARNING_PUSH \ DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ @@ -633,6 +743,12 @@ typedef DN_I32 DN_B32; #define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU32(value) #endif +enum DN_VerifyType +{ + DN_VerifyType_Nil, + DN_VerifyType_Warning, +}; + enum DN_ZMem { DN_ZMem_No, // Memory can be handed out without zero-ing it out @@ -831,7 +947,7 @@ struct DN_CallSite DN_U32 line; }; -#define DN_CALL_SITE DN_CallSite { DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } #if defined(__cplusplus) template @@ -3712,6 +3828,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. DN_GCC_WARNING_POP DN_MSVC_WARNING_POP +DN_API bool DN_VerifyArgsF (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8, char const *fmt, ...); +DN_API bool DN_VerifyArgs (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8); + #define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__) #define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__) #define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__) @@ -3721,8 +3840,8 @@ DN_API bool DN_MemStartsWith DN_API bool DN_MemEq (void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size); DN_API bool DN_MemEqUnsafe (void const *lhs, void const *rhs, DN_USize size); #if defined(__cplusplus) -template T* DN_TMemCopyObj (T *dest, T const *src, DN_USize count); -#define DN_MemCopyObj(dest, src, count) DN_TMemCopyObj(dest, src, count) +template T* DN_MemCopyObjT (T *dest, T const *src, DN_USize count); +#define DN_MemCopyObj(dest, src, count) DN_MemCopyObjT(dest, src, count) #else #define DN_MemCopyObj(dest, src, count) DN_Memcpy(dest, src, sizeof(*src) * count) #endif @@ -3783,6 +3902,7 @@ DN_API void DN_BitUnsetInplace DN_API void DN_BitSetInplace (DN_USize *flags, DN_USize bitfield); DN_API bool DN_BitIsSet (DN_USize bits, DN_USize bits_to_set); DN_API bool DN_BitIsNotSet (DN_USize bits, DN_USize bits_to_check); +DN_API bool DN_BitIsAny (DN_USize bits, DN_USize bits_to_check); #define DN_BitClearNextLSB(value) (value) & ((value) - 1) DN_API DN_I64 DN_SafeAddI64 (DN_I64 a, DN_I64 b); @@ -3955,26 +4075,26 @@ DN_API void * DN_PoolCopy #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_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_CALL_SITE, err_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_CALL_SITE, fmt, 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_CALL_SITE, fmt, ##__VA_ARGS__) +#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_CALL_SITE, exit_val, fmt, ##__VA_ARGS__) +#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_CALL_SITE, exit_val, fmt, 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_CALL_SITE, fmt, 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_CALL_SITE, fmt, ##__VA_ARGS__) +#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); @@ -4363,20 +4483,21 @@ DN_API DN_Str8 DN_Str8FromFmtANSIColourV3F32RGB255Arena // OS functionality enabled, the log callback is by default set to outputting via standard out. DN_API DN_LogPrefixSize DN_LogMakePrefix (DN_LogStyle style, DN_LogTypeParam type, DN_CallSite call_site, DN_LogDate date, char *dest, DN_USize dest_size); DN_API void DN_LogSetPrintFunc (DN_LogPrintFunc *print_func, void *user_data); -DN_API void DN_LogPrint (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_LogPrintF (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_LogPrintFV (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_LogTypeParam DN_LogTypeParamFromType (DN_LogType type); -#define DN_LogF(type, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(type), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogF(type, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(type), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogDebugF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogInfoF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Info), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogWarningF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogErrorF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogDebugF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogInfoF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogWarningF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogErrorF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogFlagF(type, flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(type), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagDebugF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagInfoF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Info), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagWarningF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagErrorF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagF(type, flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(type), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagDebugF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagInfoF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagWarningF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagErrorF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) // NOTE: OS primitives that the OS layer can provide for the base layer but is optional. @@ -4930,22 +5051,22 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, count, z_mem) #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_Yes) #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_No) - #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayMake(ptr, ptr_size, max, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, 1, z_mem) #define DN_PArrayMakeZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_Yes) #define DN_PArrayMakeNoZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_No) - #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) DN_TArrayAddArray(ptr, ptr_size, max, items, count, add) #define DN_PArrayAdd(ptr, ptr_size, max, item, add) DN_TArrayAddArray(ptr, ptr_size, max, &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_PArrayAppendAssert(ptr, ptr_size, max, item) DN_TArrayAddArrayAssert(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append, DN_CALL_SITE) + #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) @@ -4968,22 +5089,22 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No) - #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) #define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) #define DN_PArrayMakeNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No) - #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) #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_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_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_CALL_SITE) + #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) @@ -5392,7 +5513,7 @@ DN_API void DN_LeakDump_ (DN_LeakTracker *leak); // NOTE: Template implementations #if defined(__cplusplus) -template T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count) +template T *DN_MemCopyObjT(T *dest, T const *src, DN_USize count) { T* result = dest; DN_Memcpy(dest, src, sizeof(T) * count); diff --git a/Source/Base/dn_base.cpp b/Source/Base/dn_base.cpp index 1a9cbc4..ba4d8ce 100644 --- a/Source/Base/dn_base.cpp +++ b/Source/Base/dn_base.cpp @@ -16,6 +16,54 @@ enum DN_ArenaUAFCheckReportType_ DN_ArenaUAFCheckReportType_TempEndOutOfOrder, }; +DN_API bool DN_VerifyArgsF(DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8, char const *fmt, ...) +{ + bool result = expr; + if (result) + return result; + + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); + { + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + + // NOTE: Log message prefix + DN_Str8BuilderAppendF(&builder, "Verify [%.*s] failed%s", DN_Str8PrintFmt(expr_str8), fmt ? ". " : ""); + + // NOTE: Log user message + if (fmt) { + va_list args; + va_start(args, fmt); + DN_Str8BuilderAppendFV(&builder, fmt, args); + va_end(args); + } + + // NOTE: Log stack trace + if (DN_PARANOIA_LEVEL) { + if (type == DN_VerifyType_Nil) { + DN_Str8 trace = DN_Str8FromStackTraceNowArena(&scratch.arena, 128 /*limit*/, 4 /*skip*/); + DN_Str8BuilderAppendF(&builder, "\nTrace:\n "); + DN_Str8BuilderAppendRef(&builder, DN_Str8PadNewLinesArena(trace, DN_Str8Lit(" "), &scratch.arena)); + } + } + + DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, &scratch.arena); + DN_LogType log_type = type == DN_VerifyType_Nil ? DN_LogType_Error : DN_LogType_Warning; + DN_LogTypeParam log_type_param = DN_LogTypeParamFromType(log_type); + DN_LogPrintF(log_type_param, call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); + } + DN_TCScratchEnd(&scratch); + + if (type == DN_VerifyType_Nil && DN_PARANOIA_LEVEL) { + DN_DebugBreak; + } + return result; +} + +DN_API bool DN_VerifyArgs(DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8) { + bool result = DN_VerifyArgsF(type, expr, call_site, expr_str8, /*fmt=*/ 0); + return result; +} + DN_API bool DN_MemStartsWith(void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size) { bool result = false; @@ -159,7 +207,7 @@ DN_API DN_CPUReport DN_CPUGetReport() int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000; int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax); - // NOTE: Query standard/extended numbers /////////////////////////////////////////////////////// + // NOTE: Query standard/extended numbers { DN_CPUIDArgs args = {}; @@ -173,11 +221,11 @@ DN_API DN_CPUReport DN_CPUGetReport() fn_8000_[0] = DN_CPUID(args); } - // NOTE: Extract function count //////////////////////////////////////////////////////////////// + // NOTE: Extract function count int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax; int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax; - // NOTE: Enumerate all CPUID results for the known function counts ///////////////////////////// + // NOTE: Enumerate all CPUID results for the known function counts { DN_AssertF((STANDARD_FUNC_MAX_EAX + 1) <= DN_ArrayCountI(fn_0000_), "Max standard count is %d", @@ -199,14 +247,14 @@ DN_API DN_CPUReport DN_CPUGetReport() } } - // NOTE: Query CPU vendor ////////////////////////////////////////////////////////////////////// + // NOTE: Query CPU vendor { DN_Memcpy(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE); DN_Memcpy(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE); DN_Memcpy(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE); } - // NOTE: Query CPU brand /////////////////////////////////////////////////////////////////////// + // NOTE: Query CPU brand if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) { DN_Memcpy(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE); DN_Memcpy(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE); @@ -226,7 +274,7 @@ DN_API DN_CPUReport DN_CPUGetReport() DN_Assert(result.brand[sizeof(result.brand) - 1] == 0); } - // NOTE: Query CPU features ////////////////////////////////////////////////////////////////// + // NOTE: Query CPU features for (DN_USize ext_index = 0; ext_index < DN_CPUFeature_Count; ext_index++) { bool available = false; @@ -288,7 +336,7 @@ DN_API DN_CPUReport DN_CPUGetReport() case DN_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break; case DN_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break; case DN_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break; - case DN_CPUFeature_Count: DN_InvalidCodePath; break; + case DN_CPUFeature_Count: DN_AssertInvalidCodePath; break; } if (available) @@ -298,7 +346,6 @@ DN_API DN_CPUReport DN_CPUGetReport() return result; } -// NOTE: DN_TicketMutex //////////////////////////////////////////////////////////////////////////// DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex) { DN_UInt ticket = DN_AtomicAddU32(&mutex->ticket, 1); @@ -341,7 +388,7 @@ DN_API bool DN_TicketMutex_CanLock(DN_TicketMutex const *mutex, DN_UInt ticket) #endif #endif -// NOTE: DN_Bit //////////////////////////////////////////////////////////////////////////////////// +// NOTE: DN_Bit DN_API void DN_BitUnsetInplace(DN_USize *flags, DN_USize bitfield) { *flags = (*flags & ~bitfield); @@ -354,7 +401,13 @@ DN_API void DN_BitSetInplace(DN_USize *flags, DN_USize bitfield) DN_API bool DN_BitIsSet(DN_USize bits, DN_USize bits_to_set) { - auto result = DN_Cast(bool)((bits & bits_to_set) == bits_to_set); + bool result = DN_Cast(bool)((bits & bits_to_set) == bits_to_set); + return result; +} + +DN_API bool DN_BitIsAny(DN_USize bits, DN_USize bits_to_check) +{ + bool result = DN_Cast(bool)(bits & bits_to_check); return result; } @@ -780,8 +833,8 @@ DN_API void DN_ASanPoisonMemoryRegion(void const volatile *ptr, DN_USize size) __asan_poison_memory_region(ptr, size); if (DN_ASAN_VET_POISON) { - DN_HardAssert(__asan_address_is_poisoned(ptr)); - DN_HardAssert(__asan_address_is_poisoned((char *)ptr + (size - 1))); + DN_AssertAlways(__asan_address_is_poisoned(ptr)); + DN_AssertAlways(__asan_address_is_poisoned((char *)ptr + (size - 1))); } #else (void)ptr; @@ -797,7 +850,7 @@ DN_API void DN_ASanUnpoisonMemoryRegion(void const volatile *ptr, DN_USize size) #if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) __asan_unpoison_memory_region(ptr, size); if (DN_ASAN_VET_POISON) - DN_HardAssert(__asan_region_is_poisoned((void *)ptr, size) == 0); + DN_AssertAlways(__asan_region_is_poisoned((void *)ptr, size) == 0); #else (void)ptr; (void)size; @@ -873,8 +926,8 @@ static bool DN_ArenaHasPoison_(DN_MemFlags flags) static DN_MemBlock *DN_MemBlockFromMemFuncsFlags_(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_MemFuncs mem_funcs) { - bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; - bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; + bool track_alloc = (flags & DN_MemFlags_NoAllocTrack) == 0; + bool alloc_can_leak = flags & DN_MemFlags_AllocCanLeak; DN_MemBlock *result = DN_ArenaBlockFromMemFuncs_(reserve, commit, track_alloc, alloc_can_leak, mem_funcs); if (result && DN_ArenaHasPoison_(flags)) DN_ASanPoisonMemoryRegion(DN_Cast(char *) result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE); @@ -1054,21 +1107,21 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem return nullptr; try_alloc_again: - DN_MemBlock *curr = mem->curr; - bool poison = DN_ArenaHasPoison_(mem->flags); - DN_U8 real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; - DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0); - DN_U64 end_pos = offset_pos + size; - DN_U64 alloc_size = end_pos - curr->used; + DN_MemBlock *curr = mem->curr; + bool poison = DN_ArenaHasPoison_(mem->flags); + DN_U8 real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; + DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0); + DN_U64 end_pos = offset_pos + size; + DN_U64 alloc_size = end_pos - curr->used; - if (end_pos > curr->reserve) { - if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) - return nullptr; - DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE); - DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE); - if (!DN_MemListGrow(mem, new_reserve, new_commit)) - return nullptr; - goto try_alloc_again; + if (end_pos > curr->reserve) { + if (mem->flags & (DN_MemFlags_NoGrow | DN_MemFlags_UserBuffer)) + return nullptr; + DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE); + DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE); + if (!DN_MemListGrow(mem, new_reserve, new_commit)) + return nullptr; + goto try_alloc_again; } DN_USize prev_arena_commit = curr->commit; @@ -1088,8 +1141,8 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem mem->stats.hwm.commit = DN_Max(mem->stats.hwm.commit, mem->stats.info.commit); } - void *result = DN_Cast(char *) curr + offset_pos; - curr->used += alloc_size; + void *result = DN_Cast(char *) curr + offset_pos; + curr->used += alloc_size; mem->stats.info.used += alloc_size; mem->stats.hwm.used = DN_Max(mem->stats.hwm.used, mem->stats.info.used); @@ -1159,7 +1212,19 @@ DN_API void DN_MemListPopTo(DN_MemList *mem, DN_U64 init_used) if (DN_SCRUB_UNINIT_MEM_BYTE) { if (old_used > curr->used) { char *discarded = (char *)curr + curr->used; - DN_Memset(discarded, DN_SCRUB_UNINIT_MEM_BYTE, old_used - curr->used); + DN_USize scrub_size = old_used - curr->used; + + // NOTE: If we allocated memory unaligned then the pointer given to the user was aligned up + // and unpoisoned. If the user snapped a memory list position before that allocation then + // attempts to revert it, scrubbing from the memory position (which is before alignment was + // applied!) will cause this code to accidentally scrub the our poison guard bytes. So we + // unpoison the region unconditionally to ensure that is cleaned up before scrubbing. Since + // scrubbing is a debug feature and, you have it turned on _with_ ASAN then we let that + // performance penalty slide. + if (DN_ArenaHasPoison_(mem->flags)) + DN_ASanUnpoisonMemoryRegion(discarded, scrub_size); + + DN_Memset(discarded, DN_SCRUB_UNINIT_MEM_BYTE, scrub_size); } } DN_MSVC_WARNING_POP @@ -1310,7 +1375,7 @@ static void DN_ArenaUAFCheck_(DN_Arena *arena, DN_ArenaUAFCheckReportType_ type) DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" - "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", + "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s\n", DN_Str8PrintFmt(prefix), arena->uaf_guard_id, DN_Str8PrintFmt(curr_stack_trace), @@ -1697,7 +1762,7 @@ DN_API bool DN_ErrSinkEndLogError_(DN_ErrSink *err, DN_CallSite call_site, DN_St // NOTE: Log the error DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, err->arena); - DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); + DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); if (node->mode == DN_ErrSinkMode_DebugBreakOnErrorLog) DN_DebugBreak; @@ -1827,7 +1892,7 @@ DN_API void DN_TCEquip(DN_TCCore *tc) DN_API DN_TCCore *DN_TCGet() { - DN_RawAssert(g_dn_thread_context && + DN_AssertRaw(g_dn_thread_context && "This thread's thread context has not been equipped yet. Ensure that DN_TCInit(...) " "has been called to create a thread context and call DN_TCEquip(...) in the current " "thread to make it retrievable via this function"); @@ -5797,22 +5862,25 @@ DN_API void DN_LogSetPrintFunc(DN_LogPrintFunc *print_func, void *user_data) dn->print_func_context = user_data; } -DN_API void DN_LogPrint(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...) +DN_API void DN_LogPrintFV(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args) { DN_Core *dn = DN_Get(); if (type.is_u32_enum) { DN_Assert(dn->log_level_to_show_from >= 0); - if (type.u32 < DN_Cast(DN_U32)dn->log_level_to_show_from) + if (type.u32 < DN_Cast(DN_U32) dn->log_level_to_show_from) return; } - DN_LogPrintFunc *func = dn->print_func; - if (func) { - va_list args; - va_start(args, fmt); + if (func) func(type, dn->print_func_context, call_site, flags, fmt, args); - va_end(args); - } +} + +DN_API void DN_LogPrintF(DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + DN_LogPrintFV(type, call_site, flags, fmt, args); + va_end(args); } DN_API DN_LogTypeParam DN_LogTypeParamFromType(DN_LogType type) @@ -7743,7 +7811,7 @@ DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USiz DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site) { void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + DN_AssertCallSiteF(result, call_site, "Array out of space, failed to add %zu items: array=%p size=%zu max=%zu", make_count, data, *size, max); return result; } @@ -7766,7 +7834,7 @@ DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) { void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + DN_AssertCallSiteF(result, call_site, "Array out of space, failed to add %zu items: array=%p size=%zu max=%zu", elems_count, data, *size, max); return result; } @@ -8002,7 +8070,7 @@ DN_U32 DN_DSMapHash(DN_DSMap const *map, DN_DSMapKey key) DN_U32 h = seed; switch (key.type) { case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ - case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/ + case DN_DSMapKeyType_U64NoHash: DN_AssertInvalidCodePath; /*FALLTHRU*/ case DN_DSMapKeyType_Invalid: break; case DN_DSMapKeyType_Buffer: @@ -8894,7 +8962,7 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); - DN_HardAssertF( + DN_AssertAlwaysF( alloc->flags & DN_LeakAllocFlag_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 " @@ -8942,7 +9010,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); - DN_HardAssertF(alloc_entry.found, + DN_AssertAlwaysF(alloc_entry.found, "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]", @@ -8951,7 +9019,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_LeakAlloc *alloc = alloc_entry.value; if (alloc->flags & DN_LeakAllocFlag_Freed) { DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); - DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, + DN_AssertAlwaysF((alloc->flags & DN_LeakAllocFlag_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" diff --git a/Source/Base/dn_base.h b/Source/Base/dn_base.h index 4038300..bdbd67b 100644 --- a/Source/Base/dn_base.h +++ b/Source/Base/dn_base.h @@ -3,6 +3,7 @@ #if defined(_CLANGD) #define DN_STR8_AVX512F 1 + #define DN_PARANOIA_LEVEL 1 #include "../dn.h" #endif @@ -147,99 +148,160 @@ #define DN_ZeroInit {0} #endif -// NOTE: Address sanitizer -#if !defined(DN_ASAN_POISON) - #define DN_ASAN_POISON 0 -#endif +// NOTE: Macros +#define DN_Stringify(x) #x +#define DN_TokenCombine2(x, y) x ## y +#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) -#if !defined(DN_ASAN_VET_POISON) - #define DN_ASAN_VET_POISON 0 -#endif +// NOTE: Error Checking/Validating +// Asserts are useful to verify invariants in the codebase, but there's sometimes the ambiguous +// question of what should be asserted, what happens when we should have triggered an assert +// in a release build (where they are canonically turned off), what alternative mechanisms should we +// use for error checking that should be visible to non-developers. +// +// The following is an excerpt from Tom Forsyth's assertion article which he references Chris +// Hargrove's guidelines on how asserts show be used. It is quite reasonable and we model our +// primitives after based on those concepts: +// +// Logging, asserts and unit tests (https://tomforsyth1000.github.io/blog.wiki.html +// +// Assert: Immediately fatal, and not ignorable. Fundamental assumption by an engineer has been +// disproven and needs immediate handling. Requires discipline on the part of the engineer to not +// add them in situations that are actually non-fatal (rule of thumb being that if a crash would +// be almost certain to happen anyway due to the same condition, then you’re no worse off making +// an assert). +// +// Errors: Probably fatal soon, but not necessarily immediately. Basically a marker for “you are +// now in a f*cked state, you might limp along a bit, but assume nothing”. Game continues, but an +// ugly red number gets displayed onscreen for how many of these have been encountered (so when +// people send you screenshots of bugs you can then point to the red error count and blame +// accordingly). Savegames are disabled from this point so as not to make the error effectively +// permanent; you should also deliberately violate a few other TCRs as soon as an error is +// encountered in order to ensure that all parties up and down the publisher/developer chain are +// aware of how bad things are. Errors are technically “ignorable” but everyone knows that it +// might only buy you a little bit of borrowed time; these are only a small step away from the +// immediately-blocking nature of an assert, but sometimes that small step can have a big impact +// on productivity. +// +// Warnings: Used for “you did something bad, but we caught it so it’s fine (the game state is +// still okay), however it might not be fine in the future so if you want to save yourself some +// headache you should fix this sooner rather than later”. Great for content problems. Also +// displayed onscreen as a yellow number (near the red error number). You can keep these around +// for a while and triage them when their utility is called into question. +// +// Crumbs: The meta-category for a large number of “verbose” informational breadcrumb categories +// that must be explicitly enabled so you don’t clutter everything up and obscure stuff that +// matters. Note that the occurrance of certain Errors should automatically enable relevant +// categories of crumbs so that more detailed information about the aforementioned f*cked state +// will be provided during the limp-along timeframe. +// +// In the excerpt, their domain (games programming) prioritises continuity over immediate failure +// on warning and error as this allows non-developer clientele to continue using the application +// despite error laden states. This is useful in general as not all failures are critical to the +// use case that the end user is dealing with. +// +// We model `Errors` and `Warnings` as `DN_Verify` and `DN_VerifyWarning` respectively. The verify +// variants check the expression to test, log and a message and allow the developer to branch on the +// result and "recover" where appropriate. Verify checks are never compiled out. We have traditional +// `Asserts` as `DN_Assert` which can be compiled out. +// +// The article also defines what it calls a paranoia level. We `#define DN_PARANOIA_LEVEL ` +// to customise the validation layers of the codebase. See DN_PARANOIA_LEVEL in the customisation +// section for more information. +// +// In summary use each of the primitives in these situation: +// +// `DN_Assert`: Fatal and immediately needs attention and can be compiled out +// +// `DN_Verify`: Fatal or eventually fatal but not necessarily immediately, program is or will +// degenerate into an incorrect state. Is always compiled in and is visible in non-debug +// environments. +// +// `DN_VerifyWarning`: Something bad happened, but we caught it and recovered from it. Program +// state remains consistent. It is always compiled in and is visible in non-debug environments. -#define DN_ASAN_POISON_ALIGNMENT 8 - -#if !defined(DN_ASAN_POISON_GUARD_SIZE) - #define DN_ASAN_POISON_GUARD_SIZE 128 +#if !defined(DN_PARANOIA_LEVEL) + #if defined(NDEBUG) + #define DN_PARANOIA_LEVEL 0 + #else + #define DN_PARANOIA_LEVEL 1 + #endif #endif #if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) #include #endif -// NOTE: Memory +#define DN_ASAN_POISON_ALIGNMENT 8 +#if !defined(DN_ASAN_VET_POISON) + #define DN_ASAN_VET_POISON 0 +#endif + +#if !defined(DN_ASAN_POISON) + #if DN_PARANOIA_LEVEL >= 1 + #define DN_ASAN_POISON 1 + #else + #define DN_ASAN_POISON 0 + #endif +#endif + +#if !defined(DN_ASAN_POISON_GUARD_SIZE) + #define DN_ASAN_POISON_GUARD_SIZE 128 +#endif + #if !defined(DN_ARENA_TEMP_MEM_UAF_GUARD) - #define DN_ARENA_TEMP_MEM_UAF_GUARD 0 + #if DN_PARANOIA_LEVEL >= 1 + #define DN_ARENA_TEMP_MEM_UAF_GUARD 1 + #else + #define DN_ARENA_TEMP_MEM_UAF_GUARD 0 + #endif #endif #if !defined(DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT) - #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0 + #if DN_PARANOIA_LEVEL >= 2 + #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 1 + #else + #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0 + #endif #endif #if !defined(DN_SCRUB_UNINIT_MEM_BYTE) - #define DN_SCRUB_UNINIT_MEM_BYTE 0 + #if DN_PARANOIA_LEVEL >= 1 + #define DN_SCRUB_UNINIT_MEM_BYTE 0xCD + #else + #define DN_SCRUB_UNINIT_MEM_BYTE 0x00 + #endif #endif -// NOTE: Macros -#define DN_Stringify(x) #x -#define DN_TokenCombine2(x, y) x ## y -#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) - -// NOTE: Asserts -#define DN_HardAssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ - DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ +#define DN_AssertRaw(expr) do { if (!(expr)) DN_DebugBreak; } while (0) +#define DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogTypeParam log_type_ = DN_LogTypeParamFromType(DN_LogType_Error); \ + DN_LogPrintF(log_type_, call_site, DN_LogFlags_Nil, "Assertion triggered [" #expr "]. " fmt "\nTrace:\n%.*s", ## __VA_ARGS__, DN_Str8PrintFmt(trace_)); \ + DN_DebugBreak; \ + } \ } while (0) -#define DN_HardAssert(expr) DN_HardAssertF(expr, "") -// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if -// we're trying to detect some code related to the DN startup sequence. If we try to assert before -// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to -// be setup which belongs to the OS. -// -// This causes recursion errors as they call into each other. We use RawAsserts for these kind of -// checks. -#if defined(DN_NO_ASSERT) - #define DN_RawAssert(...) - #define DN_Assert(...) - #define DN_AssertOnce(...) - #define DN_AssertArgsF(...) - #define DN_AssertF(...) - #define DN_AssertFOnce(...) +#define DN_AssertAlwaysF(expr, fmt, ...) DN_AssertAlwaysCallSiteF(expr, (DN_CallSiteNow), fmt, ##__VA_ARGS__) +#define DN_AssertAlways(expr) DN_AssertAlwaysF(expr, "") +#define DN_AssertInvalidCodePathF(fmt, ...) DN_AssertAlwaysF(0, fmt, ##__VA_ARGS__) +#define DN_AssertInvalidCodePath DN_AssertInvalidCodePathF("Invalid code path") +#if DN_PARANOIA_LEVEL >= 1 +#define DN_AssertCallSiteF(expr, call_site, fmt, ...) DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ## __VA_ARGS__) +#define DN_AssertF(expr, fmt, ...) DN_AssertCallSiteF(expr, (DN_CallSiteNow), fmt, ## __VA_ARGS__) +#define DN_Assert(expr) DN_AssertAlways(expr) #else - #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) - #define DN_AssertArgsF(expr, call_site, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ - DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), \ - call_site, \ - DN_LogFlags_Nil, \ - "Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_AssertF(expr, fmt, ...) DN_AssertArgsF(expr, DN_CALL_SITE, fmt, ## __VA_ARGS__) - #define DN_AssertFOnce(expr, fmt, ...) \ - do { \ - for (static bool once_ = true; !(expr) && once_; once_ = false) \ - DN_AssertF(expr, fmt, ## __VA_ARGS__); \ - } while (0) - - #define DN_Assert(expr) DN_AssertF((expr), "") - #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") +#define DN_AssertCallSiteF(expr, call_site, fmt, ...) (void)(expr); (void)call_site +#define DN_AssertF(expr, fmt, ...) (void)(expr) +#define DN_Assert(expr) (void)(expr) #endif +#define DN_VerifyF(expr, fmt, ...) DN_VerifyArgsF(DN_VerifyType_Nil, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__) +#define DN_VerifyWarningF(expr, fmt, ...) DN_VerifyArgsF(DN_VerifyType_Warning, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__) +#define DN_Verify(expr) DN_VerifyF(expr, 0) +#define DN_VerifyWarning(expr) DN_VerifyWarningF(expr, 0) -#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) -#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") #define DN_StaticAssert(expr) \ DN_GCC_WARNING_PUSH \ DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ @@ -481,6 +543,12 @@ typedef DN_I32 DN_B32; #define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU32(value) #endif +enum DN_VerifyType +{ + DN_VerifyType_Nil, + DN_VerifyType_Warning, +}; + enum DN_ZMem { DN_ZMem_No, // Memory can be handed out without zero-ing it out @@ -679,7 +747,7 @@ struct DN_CallSite DN_U32 line; }; -#define DN_CALL_SITE DN_CallSite { DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } +#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ } #if defined(__cplusplus) template @@ -1632,6 +1700,9 @@ DN_GCC_WARNING_DISABLE(-Wunused-function) DN_GCC_WARNING_POP DN_MSVC_WARNING_POP +DN_API bool DN_VerifyArgsF (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8, char const *fmt, ...); +DN_API bool DN_VerifyArgs (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8); + #define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__) #define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__) #define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__) @@ -1641,8 +1712,8 @@ DN_API bool DN_MemStartsWith DN_API bool DN_MemEq (void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size); DN_API bool DN_MemEqUnsafe (void const *lhs, void const *rhs, DN_USize size); #if defined(__cplusplus) -template T* DN_TMemCopyObj (T *dest, T const *src, DN_USize count); -#define DN_MemCopyObj(dest, src, count) DN_TMemCopyObj(dest, src, count) +template T* DN_MemCopyObjT (T *dest, T const *src, DN_USize count); +#define DN_MemCopyObj(dest, src, count) DN_MemCopyObjT(dest, src, count) #else #define DN_MemCopyObj(dest, src, count) DN_Memcpy(dest, src, sizeof(*src) * count) #endif @@ -1703,6 +1774,7 @@ DN_API void DN_BitUnsetInplace DN_API void DN_BitSetInplace (DN_USize *flags, DN_USize bitfield); DN_API bool DN_BitIsSet (DN_USize bits, DN_USize bits_to_set); DN_API bool DN_BitIsNotSet (DN_USize bits, DN_USize bits_to_check); +DN_API bool DN_BitIsAny (DN_USize bits, DN_USize bits_to_check); #define DN_BitClearNextLSB(value) (value) & ((value) - 1) DN_API DN_I64 DN_SafeAddI64 (DN_I64 a, DN_I64 b); @@ -1875,26 +1947,26 @@ DN_API void * DN_PoolCopy #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_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_CALL_SITE, err_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_CALL_SITE, fmt, 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_CALL_SITE, fmt, ##__VA_ARGS__) +#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_CALL_SITE, exit_val, fmt, ##__VA_ARGS__) +#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_CALL_SITE, exit_val, fmt, 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_CALL_SITE, fmt, 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_CALL_SITE, fmt, ##__VA_ARGS__) +#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); @@ -2283,20 +2355,21 @@ DN_API DN_Str8 DN_Str8FromFmtANSIColourV3F32RGB255Arena // OS functionality enabled, the log callback is by default set to outputting via standard out. DN_API DN_LogPrefixSize DN_LogMakePrefix (DN_LogStyle style, DN_LogTypeParam type, DN_CallSite call_site, DN_LogDate date, char *dest, DN_USize dest_size); DN_API void DN_LogSetPrintFunc (DN_LogPrintFunc *print_func, void *user_data); -DN_API void DN_LogPrint (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_LogPrintF (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); +DN_API void DN_LogPrintFV (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_LogTypeParam DN_LogTypeParamFromType (DN_LogType type); -#define DN_LogF(type, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(type), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogF(type, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(type), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogDebugF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogInfoF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Info), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogWarningF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogErrorF(fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), DN_CALL_SITE, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogDebugF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogInfoF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogWarningF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) +#define DN_LogErrorF(fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__) -#define DN_LogFlagF(type, flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(type), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagDebugF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagInfoF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Info), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagWarningF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) -#define DN_LogFlagErrorF(flags, fmt, ...) DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), DN_CALL_SITE, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagF(type, flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(type), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagDebugF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagInfoF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagWarningF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) +#define DN_LogFlagErrorF(flags, fmt, ...) DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__) // NOTE: OS primitives that the OS layer can provide for the base layer but is optional. @@ -2850,22 +2923,22 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, count, z_mem) #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_Yes) #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_No) - #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayMake(ptr, ptr_size, max, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, 1, z_mem) #define DN_PArrayMakeZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_Yes) #define DN_PArrayMakeNoZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_No) - #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) DN_TArrayAddArray(ptr, ptr_size, max, items, count, add) #define DN_PArrayAdd(ptr, ptr_size, max, item, add) DN_TArrayAddArray(ptr, ptr_size, max, &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_PArrayAppendAssert(ptr, ptr_size, max, item) DN_TArrayAddArrayAssert(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append, DN_CALL_SITE) + #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) @@ -2888,22 +2961,22 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No) - #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) #define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) #define DN_PArrayMakeNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No) - #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem, DN_CALL_SITE) - #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes, DN_CALL_SITE) - #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No, DN_CALL_SITE) + #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem, DN_CallSiteNow) + #define DN_PArrayMakeAssertZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes, DN_CallSiteNow) + #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No, DN_CallSiteNow) #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) #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_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_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_CALL_SITE) + #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) @@ -3312,7 +3385,7 @@ DN_API void DN_LeakDump_ (DN_LeakTracker *leak); // NOTE: Template implementations #if defined(__cplusplus) -template T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count) +template T *DN_MemCopyObjT(T *dest, T const *src, DN_USize count) { T* result = dest; DN_Memcpy(dest, src, sizeof(T) * count); diff --git a/Source/Extra/dn_net_curl.cpp b/Source/Extra/dn_net_curl.cpp index 4bb962e..1f7f9a3 100644 --- a/Source/Extra/dn_net_curl.cpp +++ b/Source/Extra/dn_net_curl.cpp @@ -211,7 +211,7 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread) int running_handles = 0; CURLMcode perform_result = curl_multi_perform(curl->thread_curlm, &running_handles); if (perform_result != CURLM_OK) - DN_InvalidCodePath; + DN_AssertInvalidCodePath; // NOTE: Check pump result for (;;) { @@ -498,7 +498,7 @@ static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, D // NOTE: Setup handle for protocol switch (req->type) { - case DN_NETRequestType_Nil: DN_InvalidCodePath; break; + case DN_NETRequestType_Nil: DN_AssertInvalidCodePath; break; case DN_NETRequestType_WS: { curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); @@ -518,7 +518,7 @@ static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, D curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->args.payload.size); curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, req->args.payload.data); } else { - DN_InvalidCodePathF("Unimplemented"); + DN_AssertInvalidCodePathF("Unimplemented"); } } break; } diff --git a/Source/Extra/dn_net_emscripten.cpp b/Source/Extra/dn_net_emscripten.cpp index f770ddc..ac2f667 100644 --- a/Source/Extra/dn_net_emscripten.cpp +++ b/Source/Extra/dn_net_emscripten.cpp @@ -281,14 +281,13 @@ void DN_NET_EmcDoWSSend(DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend s { DN_AssertF(send == DN_NETWSSend_Binary || send == DN_NETWSSend_Text || send == DN_NETWSSend_Close, "Unimplemented, Emscripten only supports some of the available operations"); - - int result = 0; + int result = 0; DN_NETRequest *request_ptr = DN_Cast(DN_NETRequest *) handle.handle; if (request_ptr && request_ptr->gen == handle.gen) { DN_Assert(request_ptr->type == DN_NETRequestType_WS); DN_NETEmcRequest *emc_request = DN_Cast(DN_NETEmcRequest *) request_ptr->context[1]; switch (send) { - default: DN_InvalidCodePath; break; + default: DN_AssertInvalidCodePath; break; case DN_NETWSSend_Text: { DN_U64 pos = DN_MemListPos(request_ptr->start_response_arena.mem); DN_Str8 data_null_terminated = DN_Str8FromStr8Arena(data, &request_ptr->start_response_arena); diff --git a/Source/OS/dn_os_posix.cpp b/Source/OS/dn_os_posix.cpp index 2bb3ed9..3c49c52 100644 --- a/Source/OS/dn_os_posix.cpp +++ b/Source/OS/dn_os_posix.cpp @@ -31,7 +31,7 @@ static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect) DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc"); #endif unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags); @@ -50,7 +50,7 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_fl DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif bool result = false; if (!ptr || size == 0) @@ -66,7 +66,7 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags) DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif mprotect(ptr, size, PROT_NONE); madvise(ptr, size, MADV_FREE); @@ -75,7 +75,7 @@ DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size) DN_API void DN_OS_MemRelease(void *ptr, DN_USize size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif munmap(ptr, size); } @@ -83,7 +83,7 @@ DN_API void DN_OS_MemRelease(void *ptr, DN_USize size) DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Emscripten does not support virtual memory"); + DN_AssertInvalidCodePathF("Emscripten does not support virtual memory"); #endif if (!ptr || size == 0) return 0; @@ -199,7 +199,7 @@ DN_API DN_Date DN_OS_DateUnixTimeSToDate(DN_U64 time) DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePath; + DN_AssertInvalidCodePath; (void)buffer; (void)size; #else @@ -219,7 +219,7 @@ DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value) { - DN_AssertFOnce(false, "Unimplemented"); + DN_VerifyWarning(false, "Unimplemented function"); (void)name; (void)value; bool result = false; @@ -445,18 +445,14 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, return result; if ((access & ~(DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0))) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; return result; } if (access & DN_OSFileAccess_Execute) { result.error = true; - DN_ErrSinkAppendF( - error, - 1, - "Failed to open file '%.*s': File access flag 'execute' is not supported", - DN_Str8PrintFmt(path)); - DN_InvalidCodePath; // TODO: Not supported via fopen + DN_ErrSinkAppendF(error, 1, "Failed to open file '%.*s': File access flag 'execute' is not supported", DN_Str8PrintFmt(path)); + DN_AssertInvalidCodePath; // TODO: Not supported via fopen return result; } @@ -470,7 +466,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, case DN_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break; case DN_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break; case DN_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break; - default: DN_InvalidCodePath; break; + default: DN_AssertInvalidCodePath; break; } if (!handle) { // TODO(doyle): FileOpen flag to string @@ -747,7 +743,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, } #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Unsupported operation"); + DN_AssertInvalidCodePathF("Unsupported operation"); #endif static_assert(sizeof(pid_t) <= sizeof(handle.process), @@ -834,10 +830,9 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_ErrSink *error) { #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_InvalidCodePathF("Unsupported operation"); + DN_AssertInvalidCodePathF("Unsupported operation"); #endif - DN_AssertFOnce(args->environment.count == 0, "Unimplemented in POSIX"); - + DN_VerifyWarningF(args->environment.count == 0, "Environment variables are unimplemented in POSIX"); DN_OSExecAsyncHandle result = {}; if (cmd_line.count == 0) return result; @@ -1025,7 +1020,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, DN_U32 timeout_ms, DN_ErrSink *err) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; DN_OSExecResult result = {}; return result; } diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index 5f06947..ce14913 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -100,7 +100,7 @@ DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags) DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem) { DN_Core *dn = DN_Get(); - DN_RawAssert(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag"); + DN_AssertRaw(dn->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag"); DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0; DN_Assert(size <= DN_Cast(DWORD)(-1)); void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size); @@ -340,7 +340,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile return result; if ((access & ~DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0)) { - DN_InvalidCodePath; + DN_AssertInvalidCodePath; return result; } @@ -348,8 +348,8 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile switch (open_mode) { case DN_OSFileOpen_CreateAlways: create_flag = CREATE_ALWAYS; break; case DN_OSFileOpen_OpenIfExist: create_flag = OPEN_EXISTING; break; - case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break; - default: DN_InvalidCodePath; return result; + case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break; + default: DN_AssertInvalidCodePath; return result; } unsigned long access_mode = 0; @@ -499,7 +499,7 @@ DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path) } result.exists = true; -result.create_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreationTime); + result.create_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftCreationTime); result.last_access_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastAccessTime); result.last_write_time_in_s = DN_OS_W32FileTimeToSeconds_(&attrib_data.ftLastWriteTime); diff --git a/Source/dn.h b/Source/dn.h index b4a3388..77dd839 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -2,7 +2,8 @@ #define DN_H // NOTE: DN -// Getting Started + +// NOTE: Getting Started // Include this mega header `dn.h` and define the following symbols to `1` to conditionally // enable the interfaces for those features. Additionally in the same or different translation // unit, include `dn.cpp` with the same symbols defined to enable the implementation of these @@ -33,9 +34,10 @@ // synchronisation, memory allocation. This layer is OPTIONAL. // // - Extra layer provides helper utilities that are opt-in. These layers are OPTIONAL. -// -// Configuration -// Platform Target + +// NOTE: Configuration + +// NOTE: Platform Target // Define one of the following directives to configure this library to compile for that // platform. By default, the library will auto-detect the current host platform and select that // as the target platform. @@ -50,8 +52,8 @@ // // Will ensure that is included and the OS layer is implemented using Win32 // primitives. -// -// Static functions + +// NOTE: Static functions // All public functions in the DN library are prefixed with the macro '#define DN_API'. By // default 'DN_API' is not defined to anything. Define // @@ -60,8 +62,8 @@ // To replace all the functions prefixed with DN_API to be prefixed with 'static' ensuring that // the functions in the library do not export an entry into the linking table. // translation units. -// -// Disabling the in-built (if #define DN_H_WITH_OS 1) + +// NOTE: Disabling the in-built (if #define DN_H_WITH_OS 1) // If you are building DN for the Windows platform, is a large legacy header that // applications have to include to use Windows APIs. By default this library uses a replacement // header for all the Windows functions that it uses in the OS layer removing the need to @@ -74,16 +76,16 @@ // To instead use . DN automatically detects if is included in an earlier // translation unit and will automatically disable the in-built replacement header in which case // this does not need to be defined. -// -// Freestanding + +// NOTE: Freestanding // The base layer can be used without an OS implementation by defining DN_FREESTANDING like: // // #define DN_FREESTANDING // // This means functionality that relies on the OS like printing, memory allocation, stack traces // and so forth are disabled. -// -// ASAN Arena Poisoning + +// NOTE: ASAN Arena Poisoning // When compiled with address sanitizer (.e.g -fsanitize=address) you can optionally enable // memory region poisoning on the inbuilt arena's to catch in certain scenarios, use-after-free // @@ -93,8 +95,8 @@ // memory markup that ASAN does and so it is implemented manually by using the ASAN user-level // poisoning APIs. Similarly, since the arena recycles its own memory rather than release back // to the OS, poisoning is not as effective for arenas but every little bit helps. -// -// Scrub Uninitialised Memory + +// NOTE: Scrub Uninitialised Memory // If this macro is defined, temp memory that is returned to an arena, or allocations freed by // a pool are scrubbed to this specified byte, in absence of this bytes returned to the // allocators are left as-is or memset to 0. For example to scrub bytes to 0xCD (MSVC's @@ -105,8 +107,8 @@ // Due to the recycling of memory in arenas and pool, similarly to ASAN poisoning this reduces // the window in which a use-after-free can be detected using this guard, however every little // bit helps. -// -// Arena temp memory use-after-free (UAF) tooling + +// NOTE: Arena temp memory use-after-free (UAF) tooling // UAF Guard // Set the following preprocessor value to 1 to enable UAF protection when using // scratch/temporary memory functionality. Defaults to off, or 0 if not specified @@ -127,7 +129,7 @@ // memory block and additional book-keeping fields on each arena and their temp memory // instances. // -// UAF Tracing +// NOTE: UAF Tracing // Set the following preprocessor value to 1 to enable tracing when the UAF guard triggers. // Defaults to off, or 0 if not specified. // @@ -141,11 +143,57 @@ // Tracing incurs an additional much heavier performance penalty than the UAF guard due to // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. + +// NOTE: 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. Defaults to +// paranoia level 0 in release builds and level 1 for debug. // -// Str8 AVX512F variants -// We have some AVX512 string functions that can be enabled by defining the following +// #define DN_PARANOIA_LEVEL 1 // -// #define DN_STR8_AVX512F 1 +// Each level activates the following debug mechanisms. Note that any of the following #defines +// enabled by a paranoia level can be overridden by defining the preprocessor definition before +// the inclusion of this file. +// +// Level 0 +// `DN_Assert` calls are compiled out +// +// `DN_Verify` calls logs an error and continues +// +// `DN_VerifyWarning` calls logs a warning and continues +// +// Level 1 +// `DN_Assert` calls are compiled in +// +// `DN_Verify` calls a debug trap rather than just logging and continuing +// +// `DN_Verify` calls dump a stack trace when triggered +// +// `DN_ASAN_POISON` is set. When an arena allocates memory unallocated bytes from the +// memory owned by the arena are manually poisoned using ASAN. A fault will be triggered if +// the memory is written to (UAF e.g. use-after-free). Address sanitizer must be enabled or +// otherwise this is a no-op. This incurs a performance penalty on-top of the overhead of +// running ASAN on your binary as recycling memory calls into ASAN to poison the region. +// +// `DN_ARENA_TEMP_MEM_UAF_GUARD` is set. When an arena uses temporary memory it will record +// the active temporary memory region and compare them when allocating to ensure that +// memory is allocated in the active region otherwise a UAF fault is triggered. This has a +// small runtime performance penalty. +// +// `DN_SCRUB_UNINIT_MEM_BYTE` is set to `0xCD`. When memory is cleared in an arena or a +// pool backed by an arena upon deallocation if the `DN_ZMem_Yes` flag is passed then the +// bytes are scrubbed to this byte to make UAF more salient. +// +// Level 2 +// `DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT` is set. When an arena uses temporary memory +// regions that region's a stack trace of the call site is recorded. This is very expensive +// but when a temporary memory region is used after it has been deallocated, a full stack +// trace diagnostic is available of where the various regions where created and freed. +// +// NOTE: Str8 AVX512F variants +// We have some AVX512 string functions that can be enabled by defining the following +// +// #define DN_STR8_AVX512F 1 #include "Base/dn_base.h" diff --git a/build.bat b/build.bat index 81666aa..9abd1b4 100644 --- a/build.bat +++ b/build.bat @@ -50,10 +50,15 @@ pushd %build_dir% ) set msvc_cmd=!msvc_cmd! -link + 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 ... 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) + call cl %script_dir%\single_header_generator.cpp -Z7 -nologo -link -D USE_SINGLE_HEADER || echo Single header generator build failed&& exit /b 1 + call %build_dir%\single_header_generator.exe %script_dir%\Source %script_dir%\Single-Header -D USE_SINGLE_HEADER || echo Single header generation failed&& exit /b 1 ) where /q clang-cl && ( diff --git a/single_header_generator.cpp b/single_header_generator.cpp index f398c7f..20727d0 100644 --- a/single_header_generator.cpp +++ b/single_header_generator.cpp @@ -1,14 +1,12 @@ -#define USE_SINGLE_HEADER 1 - #define DN_H_WITH_OS 1 #define DN_H_WITH_CORE 1 -#if USE_SINGLE_HEADER +#if defined(USE_SINGLE_HEADER) #include "Single-Header/dn_single_header.h" #else #include "Source/dn.h" #endif -#if USE_SINGLE_HEADER +#if defined(USE_SINGLE_HEADER) #include "Single-Header/dn_single_header.cpp" #else #include "Source/dn.cpp"