diff --git a/Misc/dqn_unit_tests.cpp b/Misc/dqn_unit_tests.cpp index b4a45bb..c1da23e 100644 --- a/Misc/dqn_unit_tests.cpp +++ b/Misc/dqn_unit_tests.cpp @@ -746,6 +746,18 @@ Dqn_UTest TestIntrinsics() // atomics/intrinsics are implemented using macros we ensure the macro was // written properly with these tests. + DQN_MSVC_WARNING_PUSH + + // NOTE: MSVC SAL complains that we are using Interlocked functionality on + // variables it has detected as *not* being shared across threads. This is + // fine, we're just running some basic tests, so permit it. + // + // Warning 28112 is a knock-on effect of this that it doesn't like us + // reading the value of the variable that has been used in an Interlocked + // function locally. + DQN_MSVC_WARNING_DISABLE(28113) // Accessing a local variable val via an Interlocked function. + DQN_MSVC_WARNING_DISABLE(28112) // A variable (val) which is accessed via an Interlocked function must always be accessed via an Interlocked function. See line 759. + DQN_UTEST_GROUP(test, "Dqn_Atomic") { DQN_UTEST_TEST("Dqn_Atomic_AddU32") { uint32_t val = 0; @@ -775,7 +787,7 @@ Dqn_UTest TestIntrinsics() long a = 0; long b = 111; Dqn_Atomic_SetValue32(&a, b); - DQN_UTEST_ASSERTF(&test, a == b, "a: %lu, b: %lu", a, b); + DQN_UTEST_ASSERTF(&test, a == b, "a: %ld, b: %ld", a, b); } DQN_UTEST_TEST("Dqn_Atomic_SetValue64") { @@ -797,6 +809,7 @@ Dqn_UTest TestIntrinsics() Dqn_CompilerWriteBarrierAndCPUWriteFence; Dqn_UTest_End(&test); } + DQN_MSVC_WARNING_POP return test; } @@ -1067,7 +1080,7 @@ Dqn_UTest TestOS() DQN_UTEST_TEST("Query executable directory") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 result = Dqn_OS_EXEDir(scratch.allocator); + Dqn_String8 result = Dqn_OS_EXEDir(scratch.arena); DQN_UTEST_ASSERT(&test, Dqn_String8_IsValid(result)); DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STRING_FMT(result)); } @@ -1233,7 +1246,7 @@ Dqn_UTest TestString8() DQN_UTEST_GROUP(test, "Dqn_String8") { DQN_UTEST_TEST("Initialise with string literal w/ macro") { Dqn_String8 string = DQN_STRING8("AB"); - DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64d", string.size); + DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); } @@ -1241,17 +1254,17 @@ Dqn_UTest TestString8() DQN_UTEST_TEST("Initialise with format string") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 string = Dqn_String8_InitF(scratch.allocator, "%s", "AB"); - DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64d", string.size); + DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); - DQN_UTEST_ASSERTF(&test, string.data[2] == 0, "string[2]: %c", string.data[2]); + DQN_UTEST_ASSERTF(&test, string.data[2] == 0, "string[2]: %c", string.data[2]); } DQN_UTEST_TEST("Copy string") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 string = DQN_STRING8("AB"); Dqn_String8 copy = Dqn_String8_Copy(scratch.allocator, string); - DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %I64d", copy.size); + DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %I64u", copy.size); DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]); DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]); DQN_UTEST_ASSERTF(&test, copy.data[2] == 0, "copy[2]: %c", copy.data[2]); @@ -1265,7 +1278,7 @@ Dqn_UTest TestString8() DQN_UTEST_TEST("Allocate string from arena") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 string = Dqn_String8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No); - DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64d", string.size); + DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); } // NOTE: Dqn_CString8_Trim[Prefix/Suffix] @@ -1652,15 +1665,12 @@ Dqn_UTest TestVArray() // from the memory block (and hence the array) contiguously // when the size of the object is not aligned with the required // alignment of the object. - - #if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable: 4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier struct alignas(8) UnalignedObject { char data[511]; }; - #pragma warning(pop) - #endif // _MSC_VER + DQN_MSVC_WARNING_POP Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_VArray array = Dqn_VArray_InitByteSize(scratch.arena, DQN_KILOBYTES(64)); @@ -1699,35 +1709,35 @@ Dqn_UTest TestWin() Dqn_UTest test = {}; DQN_UTEST_GROUP(test, "Dqn_Win") { DQN_UTEST_TEST("String8 to String16 size required") { - int result = Dqn_Win_String8ToCString16(DQN_STRING8("a"), nullptr, 0); + int result = Dqn_Win_String8ToString16Buffer(DQN_STRING8("a"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result); } DQN_UTEST_TEST("String16 to String8 size required") { - int result = Dqn_Win_String16ToCString8(DQN_STRING16(L"a"), nullptr, 0); + int result = Dqn_Win_String16ToString8Buffer(DQN_STRING16(L"a"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result); } DQN_UTEST_TEST("String8 to String16 size required") { - int result = Dqn_Win_String8ToCString16(DQN_STRING8("String"), nullptr, 0); + int result = Dqn_Win_String8ToString16Buffer(DQN_STRING8("String"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result); } DQN_UTEST_TEST("String16 to String8 size required") { - int result = Dqn_Win_String16ToCString8(DQN_STRING16(L"String"), nullptr, 0); + int result = Dqn_Win_String16ToString8Buffer(DQN_STRING16(L"String"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result); } DQN_UTEST_TEST("String8 to String16") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 const INPUT = DQN_STRING8("String"); - int size_required = Dqn_Win_String8ToCString16(INPUT, nullptr, 0); + int size_required = Dqn_Win_String8ToString16Buffer(INPUT, nullptr, 0); wchar_t *string = Dqn_Arena_NewArray(scratch.arena, wchar_t, size_required + 1, Dqn_ZeroMem_No); // Fill the string with error sentinels, which ensures the string is zero terminated DQN_MEMSET(string, 'Z', size_required + 1); - int size_returned = Dqn_Win_String8ToCString16(INPUT, string, size_required + 1); + int size_returned = Dqn_Win_String8ToString16Buffer(INPUT, string, size_required + 1); wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0}; DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); @@ -1738,19 +1748,45 @@ Dqn_UTest TestWin() DQN_UTEST_TEST("String16 to String8: No null-terminate") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String16 INPUT = DQN_STRING16(L"String"); - int size_required = Dqn_Win_String16ToCString8(INPUT, nullptr, 0); + int size_required = Dqn_Win_String16ToString8Buffer(INPUT, nullptr, 0); char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No); // Fill the string with error sentinels, which ensures the string is zero terminated DQN_MEMSET(string, 'Z', size_required + 1); - int size_returned = Dqn_Win_String16ToCString8(INPUT, string, size_required + 1); + int size_returned = Dqn_Win_String16ToString8Buffer(INPUT, string, size_required + 1); char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); DQN_UTEST_ASSERTF(&test, size_returned == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, DQN_ARRAY_UCOUNT(EXPECTED) - 1); DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0); } + + DQN_UTEST_TEST("String8 to String16 arena") { + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String8 const INPUT = DQN_STRING8("String"); + Dqn_String16 string16 = Dqn_Win_String8ToString16(scratch.arena, INPUT); + + int size_returned = Dqn_Win_String8ToString16Buffer(INPUT, nullptr, 0); + wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0}; + + DQN_UTEST_ASSERTF(&test, string16.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string16.size, size_returned); + DQN_UTEST_ASSERTF(&test, string16.size == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", DQN_CAST(int)string16.size, DQN_ARRAY_UCOUNT(EXPECTED) - 1); + DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string16.data, sizeof(EXPECTED)) == 0); + } + + DQN_UTEST_TEST("String16 to String8: No null-terminate arena") { + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String16 INPUT = DQN_STRING16(L"String"); + Dqn_String8 string8 = Dqn_Win_String16ToString8(scratch.arena, INPUT); + + int size_returned = Dqn_Win_String16ToString8Buffer(INPUT, nullptr, 0); + char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; + + DQN_UTEST_ASSERTF(&test, string8.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string8.size, size_returned); + DQN_UTEST_ASSERTF(&test, string8.size == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", DQN_CAST(int)string8.size, DQN_ARRAY_UCOUNT(EXPECTED) - 1); + DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string8.data, sizeof(EXPECTED)) == 0); + } } return test; } diff --git a/build.bat b/build.bat index fd50731..8f6abdd 100644 --- a/build.bat +++ b/build.bat @@ -5,8 +5,7 @@ set code_dir=%~dp0 if not exist Build mkdir Build pushd Build - REM Flags - REM ------------------------------------------------------------------------ + REM Flags ================================================================== REM MT Static CRT REM EHa- Disable exception handling REM GR- Disable C RTTI @@ -15,24 +14,22 @@ pushd Build REM Z7 Combine multi-debug files to one debug file REM wd4201 Nonstandard extension used: nameless struct/union REM Tp Treat header file as CPP source file - set compile_flags=-MT -EHa -GR- -Od -Oi -Z7 -wd4201 -D DQN_TEST_WITH_MAIN -nologo + set compile_flags=-MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -WX -D DQN_TEST_WITH_MAIN -nologo -analyze set linker_flags=-link -nologo set msvc_flags=-fsanitize=address set clang_flags=-fsanitize=address,undefined - REM Compiler: MSVC cl - REM ------------------------------------------------------------------------ + REM Compiler: MSVC cl ====================================================== where /q cl || ( echo [ERROR] cl is not found, please put MSVC's cl on the path exit /b 1 ) - cl %compile_flags% %msvc_flags% %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_msvc %link_flags% + cl %compile_flags% %msvc_flags% %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_msvc %link_flags% || exit /b 1 - REM Compiler: clang-cl - REM ------------------------------------------------------------------------ + REM Compiler: clang-cl ===================================================== where /q clang-cl || ( echo [WARN] Optional clang compile via clang-cl if it's in the path, please put clang-cl on the path for this feature - exit /b 1 + exit /b 0 ) clang-cl -D DQN_TEST_WITH_MAIN %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_clang -link popd diff --git a/dqn_base.h b/dqn_base.h index 2b150a3..3791791 100644 --- a/dqn_base.h +++ b/dqn_base.h @@ -48,6 +48,18 @@ #endif #endif +#if defined(DQN_COMPILER_W32_MSVC) + #define DQN_FMT_STRING_ANNOTATE _Printf_format_string_ + #define DQN_MSVC_WARNING_PUSH __pragma(warning(push)) + #define DQN_MSVC_WARNING_DISABLE(...) __pragma(warning(disable: ##__VA_ARGS__)) + #define DQN_MSVC_WARNING_POP __pragma(warning(pop)) +#else + #define DQN_FMT_STRING_ANNOTATE + #define DQN_MSVC_WARNING_PUSH + #define DQN_MSVC_WARNING_DISABLE(...) + #define DQN_MSVC_WARNING_POP +#endif + // NOTE: [$MACR] Macros ============================================================================ #define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++) #define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++) diff --git a/dqn_containers.h b/dqn_containers.h index ca94ce9..64177e3 100644 --- a/dqn_containers.h +++ b/dqn_containers.h @@ -376,12 +376,12 @@ template struct Dqn_List }; // NOTE: API ======================================================================================= -DQN_API template Dqn_List Dqn_List_InitWithArena(Dqn_Arena *arena, Dqn_usize chunk_size = 128); -DQN_API template T * Dqn_List_At (Dqn_List *list, Dqn_usize index, Dqn_ListChunk *at_chunk); -DQN_API template bool Dqn_List_Iterate (Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index); +template Dqn_List Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size); +template T * Dqn_List_At (Dqn_List *list, Dqn_usize index, Dqn_ListChunk *at_chunk); +template bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index); -DQN_API template T * Dqn_List_Make (Dqn_List *list, Dqn_usize count); -DQN_API template T * Dqn_List_Add (Dqn_List *list, Dqn_usize count); +template T * Dqn_List_Make (Dqn_List *list, Dqn_usize count); +template T * Dqn_List_Add (Dqn_List *list, T const &value); #endif // !defined(DQN_NO_LIST) #if !defined(DQN_NO_VARRAY) diff --git a/dqn_debug.cpp b/dqn_debug.cpp index 27a2556..8d1f62d 100644 --- a/dqn_debug.cpp +++ b/dqn_debug.cpp @@ -208,15 +208,15 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator, "%.*s" // reset " %.*s" // file name ":%05u ", // line number - time.date_size - 2, time.date + 2, - time.hms_size, time.hms, - colour_esc.size, colour_esc.data, - bold_esc.size, bold_esc.data, - type.size, type.data, - type_padding, "", - reset_esc.size, reset_esc.data, - file_name.size, file_name.data, - call_site.line); + DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date + DQN_CAST(uint32_t)time.hms_size, time.hms, // hms + DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour + DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold + DQN_CAST(uint32_t)type.size, type.data, // type + DQN_CAST(uint32_t)type_padding, "", // type padding + DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset + DQN_CAST(uint32_t)file_name.size, file_name.data, // file name + call_site.line); // line number header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size; } @@ -244,13 +244,12 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use (void)log_type; (void)user_data; - // NOTE: Open log file for appending if requested - // ========================================================================= + // NOTE: Open log file for appending if requested ========================== Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex); if (g_dqn_library->log_to_file && !g_dqn_library->log_file) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) - Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.allocator); + Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.arena); #else Dqn_String8 exe_dir = DQN_STRING8("."); #endif @@ -259,8 +258,7 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use } Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex); - // NOTE: Generate the log header - // ========================================================================= + // NOTE: Generate the log header =========================================== Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator, !g_dqn_library->log_no_colour, @@ -270,12 +268,11 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use fmt, args); - // NOTE: Print log - // ========================================================================= + // NOTE: Print log ========================================================= Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line); Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex); - if (g_dqn_library->log_to_file) { + if (g_dqn_library->log_to_file && g_dqn_library->log_file) { fprintf(DQN_CAST(FILE *)g_dqn_library->log_file, "%.*s\n", DQN_STRING_FMT(log_line)); } Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex); diff --git a/dqn_external.cpp b/dqn_external.cpp index 9dea54e..bd1bf92 100644 --- a/dqn_external.cpp +++ b/dqn_external.cpp @@ -1,6 +1,11 @@ // NOTE: [$BSTK] b_stacktrace ====================================================================== +DQN_MSVC_WARNING_PUSH +DQN_MSVC_WARNING_DISABLE(6308) // b_stacktrace.h|147 'realloc' might return null pointer: assigning null pointer to 'b->buf', which is passed as an argument to 'realloc', will cause the original memory block to be leaked. +DQN_MSVC_WARNING_DISABLE(6011) // b_stacktrace.h|244 Dereferencing NULL pointer 'cur'. : Lines: 183, 184, 185, 186, 187, 188, 189, 191, 196, 198, 208, 209, 210, 211, 212, 213, 214, 229, 230, 231, 237, 244 +DQN_MSVC_WARNING_DISABLE(6387) // b_stacktrace.h|284 'symbol' could be '0'.: Lines: 256, 257, 258, 259, 260, 261, 262, 264, 265, 266, 267, 268, 270, 276, 281, 282, 284 #define B_STACKTRACE_IMPL #include "b_stacktrace.h" +DQN_MSVC_WARNING_POP // NOTE: [$STBS] stb_sprintf ======================================================================= #if !defined(DQN_STB_SPRINTF_HEADER_ONLY) diff --git a/dqn_external.h b/dqn_external.h index 3e6bfba..8ea1654 100644 --- a/dqn_external.h +++ b/dqn_external.h @@ -5,6 +5,7 @@ #include #include #endif + #define B_STACKTRACE_API static #include "b_stacktrace.h" @@ -17,13 +18,14 @@ #include // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc #include // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc #if !defined(DQN_NO_WINNET) + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6553) // wininet.h|940 warning| The annotation for function 'InternetConnectA' on _Param_(8) does not apply to a value type. #include // Dqn_Win_Net -> InternetConnect ... etc + DQN_MSVC_WARNING_POP #endif // DQN_NO_WINNET #elif !defined(_INC_WINDOWS) - #if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(push) - #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union - #endif + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union #define MAX_PATH 260 // NOTE: Wait/Synchronization @@ -358,9 +360,7 @@ /*HINSTANCE*/ void * __stdcall ShellExecuteA (void *hwnd, char const *lpOperation, char const *lpFile, char const *lpParameters, char const *lpDirectory, int nShowCmd); /*BOOL*/ int __stdcall ShowWindow (void *hWnd, int nCmdShow); } - #if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(pop) - #endif + DQN_MSVC_WARNING_POP #endif // !defined(_INC_WINDOWS) #elif defined(DQN_OS_UNIX) #include // errno diff --git a/dqn_helpers.cpp b/dqn_helpers.cpp index c3b1b5a..9df2057 100644 --- a/dqn_helpers.cpp +++ b/dqn_helpers.cpp @@ -761,10 +761,9 @@ DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator) // dividing by 10, so we write it in, then reverse it out after all is // done. Dqn_U64String temp = {}; - for (size_t digit_count = 0; val > 0; digit_count++) { - if (separator && (digit_count != 0) && (digit_count % 3 == 0)) { + for (Dqn_usize digit_count = 0; val > 0; digit_count++) { + if (separator && (digit_count != 0) && (digit_count % 3 == 0)) temp.data[temp.size++] = separator; - } auto digit = DQN_CAST(char)(val % 10); temp.data[temp.size++] = '0' + digit; @@ -772,10 +771,14 @@ DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator) } // NOTE: Reverse the string - for (size_t temp_index = temp.size - 1; temp_index < temp.size; temp_index--) { + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop + DQN_MSVC_WARNING_DISABLE(6385) // Reading invalid data from 'temp.data' NOTE(doyle): Unsigned overflow is valid for loop termination + for (Dqn_usize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) { char ch = temp.data[temp_index]; result.data[result.size++] = ch; } + DQN_MSVC_WARNING_POP } return result; diff --git a/dqn_math.h b/dqn_math.h index 8a693e7..b4e000e 100644 --- a/dqn_math.h +++ b/dqn_math.h @@ -1,7 +1,5 @@ -#if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(push) - #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union -#endif +DQN_MSVC_WARNING_PUSH +DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union #if !defined(DQN_NO_V2) // NOTE: [$VEC2] Vector2 =========================================================================== @@ -159,11 +157,6 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a); #if !defined(DQN_NO_V4) // NOTE: [$VEC4] Vector4 =========================================================================== - -#if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(push) - #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union -#endif union Dqn_V4 { struct { Dqn_f32 x, y, z, w; }; @@ -194,10 +187,6 @@ Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_f32 rhs); Dqn_V4 &operator*=(Dqn_V4 &lhs, int32_t rhs); Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 rhs); Dqn_V4 &operator+=(Dqn_V4 &lhs, Dqn_V4 rhs); - -#if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(pop) -#endif #endif // !defined(DQN_NO_V4) #if !defined(DQN_NO_M4) @@ -308,7 +297,4 @@ DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutC // NOTE: [$MATH] Other ============================================================================= DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); - -#if defined(DQN_COMPILER_W32_MSVC) - #pragma warning(pop) -#endif +DQN_MSVC_WARNING_POP diff --git a/dqn_memory.cpp b/dqn_memory.cpp index 1a5c8ec..56bbf40 100644 --- a/dqn_memory.cpp +++ b/dqn_memory.cpp @@ -102,7 +102,14 @@ DQN_API bool Dqn_VMem_Commit(void *ptr, Dqn_usize size, uint32_t page_flags) DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size) { #if defined(DQN_OS_WIN32) + + // NOTE: This is a decommit call, which is explicitly saying to free the + // pages but not the VADs, you would use VMem_Release to release everything. + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6250) // Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks. VirtualFree(ptr, size, MEM_DECOMMIT); + DQN_MSVC_WARNING_POP + #elif defined(DQN_OS_UNIX) mprotect(ptr, size, PROT_NONE); madvise(ptr, size, MADV_FREE); @@ -144,8 +151,9 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags) #if defined(DQN_NO_WIN) DQN_ASSERTF(result, "VirtualProtect failed"); #else - Dqn_WinErrorMsg error = Dqn_Win_LastError(); - DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, error.size, error.data); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, DQN_STRING_FMT(error.msg)); #endif } #else @@ -312,6 +320,7 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, Dqn_usize size, uint8_t align, D if (!arena->curr) { if (!Dqn_Arena_Grow(arena, size, size /*commit*/, 0 /*flags*/)) return result; + DQN_ASSERT(arena->curr); } result = Dqn_MemBlock_Alloc(arena->curr, size, align, zero_mem); @@ -401,7 +410,7 @@ DQN_API void Dqn_Arena_EndTempMemory(Dqn_ArenaTempMemory temp_memory, bool cance arena->tail->next = nullptr; // NOTE: Reset the usage of all the blocks between the tail and current block's - for (Dqn_MemBlock *block = arena->tail; block != arena->curr; block = block->prev) + for (Dqn_MemBlock *block = arena->tail; block && (block != arena->curr); block = block->prev) block->used = 0; } diff --git a/dqn_platform.cpp b/dqn_platform.cpp index a414bd4..c571fab 100644 --- a/dqn_platform.cpp +++ b/dqn_platform.cpp @@ -94,7 +94,7 @@ DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int return (char *)buf; } -DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, char const *fmt, ...) +DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -102,7 +102,7 @@ DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, char const *fmt, ...) va_end(args); } -DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...) +DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -110,13 +110,13 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, va_end(args); } -DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, char const *fmt, va_list args) +DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { char buffer[STB_SPRINTF_MIN]; STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args); } -DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args) +DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { if (fmt) { if (style.colour) @@ -135,7 +135,7 @@ DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string) Dqn_Print_Std(std_handle, DQN_STRING8("\n")); } -DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...) +DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -143,7 +143,7 @@ DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...) va_end(args); } -DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, char const *fmt, va_list args) +DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { Dqn_Print_StdFV(std_handle, fmt, args); Dqn_Print_Std(std_handle, DQN_STRING8("\n")); @@ -155,7 +155,7 @@ DQN_API void Dqn_Print_StdLnStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Print_Std(std_handle, DQN_STRING8("\n")); } -DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...) +DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -163,7 +163,7 @@ DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style va_end(args); } -DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args) +DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { Dqn_Print_StdFVStyle(std_handle, style, fmt, args); Dqn_Print_Std(std_handle, DQN_STRING8("\n")); @@ -203,7 +203,7 @@ DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint #if !defined(DQN_NO_FS) // NOTE: [$FSYS] Dqn_Fs ============================================================================ #if defined(DQN_OS_WIN32) -DQN_API uint64_t Dqn__WinFileTimeToSeconds(FILETIME const *time) +DQN_API uint64_t Dqn_Win__FileTimeToSeconds(FILETIME const *time) { ULARGE_INTEGER time_large_int = {}; time_large_int.u.LowPart = time->dwLowDateTime; @@ -216,12 +216,15 @@ DQN_API uint64_t Dqn__WinFileTimeToSeconds(FILETIME const *time) DQN_API bool Dqn_Fs_Exists(Dqn_String8 path) { bool result = false; + if (!Dqn_String8_IsValid(path)) + return result; + #if defined(DQN_OS_WIN32) - wchar_t path16[DQN_OS_WIN32_MAX_PATH]; - int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); - if (path16_size) { + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; - if (GetFileAttributesExW(path16, GetFileExInfoStandard, &attrib_data)) { + if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) && !(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } @@ -240,12 +243,15 @@ DQN_API bool Dqn_Fs_Exists(Dqn_String8 path) DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path) { bool result = false; + if (!Dqn_String8_IsValid(path)) + return result; + #if defined(DQN_OS_WIN32) - wchar_t path16[DQN_OS_WIN32_MAX_PATH]; - int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); - if (path16_size) { + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; - if (GetFileAttributesExW(path16, GetFileExInfoStandard, &attrib_data)) { + if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) && (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } @@ -264,17 +270,21 @@ DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path) DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path) { Dqn_FsInfo result = {}; + if (!Dqn_String8_IsValid(path)) + return result; + #if defined(DQN_OS_WIN32) + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; - wchar_t path16[DQN_OS_WIN32_MAX_PATH]; - Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); - if (!GetFileAttributesExW(path16, GetFileExInfoStandard, &attrib_data)) + if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) return result; result.exists = true; - result.create_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftCreationTime); - result.last_access_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftLastAccessTime); - result.last_write_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftLastWriteTime); + result.create_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftCreationTime); + result.last_access_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftLastAccessTime); + result.last_write_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftLastWriteTime); LARGE_INTEGER large_int = {}; large_int.u.HighPart = DQN_CAST(int32_t)attrib_data.nFileSizeHigh; @@ -314,18 +324,18 @@ DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite) bool result = false; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 src16 = Dqn_Win_String8ToString16Allocator(src, Dqn_Arena_Allocator(scratch.arena)); - Dqn_String16 dest16 = Dqn_Win_String8ToString16Allocator(dest, Dqn_Arena_Allocator(scratch.arena)); + Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src); + Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest); int fail_if_exists = overwrite == false; result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; if (!result) { - Dqn_WinErrorMsg error = Dqn_Win_LastError(); - Dqn_Log_ErrorF("Copy file failed [src=\"%.*s\", dest=\"%.*s\", reason=\"%.*s\"]", + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("Failed to copy the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s", DQN_STRING_FMT(src), DQN_STRING_FMT(dest), - DQN_STRING_FMT(error)); + DQN_STRING_FMT(error.msg)); } #elif defined(DQN_OS_UNIX) @@ -355,11 +365,9 @@ DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); bool result = true; - Dqn_usize path_indexes_size = 0; - uint16_t path_indexes[64] = {}; #if defined(DQN_OS_WIN32) - Dqn_String16 path16 = Dqn_Win_String8ToString16Allocator(path, Dqn_Arena_Allocator(scratch.arena)); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); // NOTE: Go back from the end of the string to all the directories in the // string, and try to create them. Since Win32 API cannot create @@ -373,56 +381,58 @@ DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path) // If we find a file at some point in the path we fail out because the // series of directories can not be made if a file exists with the same // name. - for (Dqn_usize index = path16.size - 1; index < path16.size; index--) { + for (Dqn_usize index = 0; index < path16.size; index++) { bool first_char = index == (path16.size - 1); wchar_t ch = path16.data[index]; if (ch == '/' || ch == '\\' || first_char) { - WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; wchar_t temp = path16.data[index]; + if (!first_char) + path16.data[index] = 0; // Temporarily null terminate it - if (!first_char) path16.data[index] = 0; // Temporarily null terminate it + WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; bool successful = GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data); // Check - if (!first_char) path16.data[index] = temp; // Undo null termination if (successful) { if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - // NOTE: We found a directory, we can stop here and start - // building up all the directories that didn't exist up to - // this point. - break; + // NOTE: The directory exists, continue iterating the path } else { - // NOTE: There's something that exists in at this path, but - // it's not a directory. This request to make a directory is - // invalid. + // NOTE: There's some kind of file that exists at the path + // but it's not a directory. This request to make a + // directory is invalid. return false; } } else { // NOTE: There's nothing that exists at this path, we can create // a directory here - path_indexes[path_indexes_size++] = DQN_CAST(uint16_t)index; + result |= (CreateDirectoryW(path16.data, nullptr) == 0); } + + if (!first_char) + path16.data[index] = temp; // Undo null termination } } - for (Dqn_usize index = path_indexes_size - 1; result && index < path_indexes_size; index--) { - uint16_t path_index = path_indexes[index]; - wchar_t temp = path16.data[path_index]; - - if (index != 0) path16.data[path_index] = 0; - result |= (CreateDirectoryW(path16.data, nullptr) == 0); - if (index != 0) path16.data[path_index] = temp; - } - #elif defined(DQN_OS_UNIX) + + // TODO(doyle): Implement this without using the path indexes, it's not + // necessary. See Windows implementation. + Dqn_usize path_indexes_size = 0; + uint16_t path_indexes[64] = {}; + Dqn_String8 copy = Dqn_String8_Copy(scratch.arena, path); for (Dqn_usize index = copy.size - 1; index < copy.size; index--) { bool first_char = index == (copy.size - 1); char ch = copy.data[index]; if (ch == '/' || first_char) { char temp = copy.data[index]; - if (!first_char) copy.data[index] = 0; // Temporarily null terminate it + + if (!first_char) + copy.data[index] = 0; // Temporarily null terminate it + bool is_file = Dqn_Fs_Exists(copy); - if (!first_char) copy.data[index] = temp; // Undo null termination + + if (!first_char) + copy.data[index] = temp; // Undo null termination if (is_file) { // NOTE: There's something that exists in at this path, but @@ -466,8 +476,8 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 src16 = Dqn_Win_String8ToString16Allocator(src, Dqn_Arena_Allocator(scratch.arena)); - Dqn_String16 dest16 = Dqn_Win_String8ToString16Allocator(dest, Dqn_Arena_Allocator(scratch.arena)); + Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src); + Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest); unsigned long flags = MOVEFILE_COPY_ALLOWED; if (overwrite) { @@ -476,10 +486,12 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) result = MoveFileExW(src16.data, dest16.data, flags) != 0; if (!result) { - Dqn_Log_ErrorF("Failed to move file [from=%.*s, to=%.*s, reason=%.*s]", + Dqn_ThreadScratch inner_scratch = Dqn_Thread_GetScratch(scratch.arena); + Dqn_WinError error = Dqn_Win_LastError(inner_scratch.arena); + Dqn_Log_ErrorF("Failed to move the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s", DQN_STRING_FMT(src), DQN_STRING_FMT(dest), - DQN_STRING_FMT(Dqn_Win_LastError())); + DQN_STRING_FMT(error.msg)); } #elif defined(DQN_OS_UNIX) @@ -504,13 +516,16 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) DQN_API bool Dqn_Fs_Delete(Dqn_String8 path) { bool result = false; + if (!Dqn_String8_IsValid(path)) + return result; + #if defined(DQN_OS_WIN32) - wchar_t path16[DQN_OS_WIN32_MAX_PATH]; - int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); - if (path16_size) { - result = DeleteFileW(path16); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + if (path16.size) { + result = DeleteFileW(path16.data); if (!result) - result = RemoveDirectoryW(path16); + result = RemoveDirectoryW(path16.data); } #elif defined(DQN_OS_UNIX) result = remove(path.data) == 0; @@ -534,15 +549,12 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi (void)file_size; #if defined(DQN_OS_WIN32) - // NOTE: Convert to UTF16 - // ------------------------------------------------------------------------- - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); - Dqn_String8 path8 = Dqn_String8_Init(path, path_size); - Dqn_String16 path16 = Dqn_Win_String8ToString16Allocator(path8, scratch.allocator); - Dqn_WinErrorMsg error_msg = {}; + // NOTE: Convert to UTF16 ================================================== + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); + Dqn_String8 path8 = Dqn_String8_Init(path, path_size); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path8); - // NOTE: Get the file handle - // ------------------------------------------------------------------------- + // NOTE: Get the file handle =============================================== void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ GENERIC_READ, /*DWORD dwShareMode*/ 0, @@ -551,8 +563,8 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi /*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_READONLY, /*HANDLE hTemplateFile*/ nullptr); if (file_handle == INVALID_HANDLE_VALUE) { - Dqn_Win_LastErrorToBuffer(&error_msg); - Dqn_Log_ErrorF("Failed to open file for reading [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error_msg)); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("Failed to open file for reading [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg)); return nullptr; } DQN_DEFER { CloseHandle(file_handle); }; @@ -561,8 +573,8 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi // ------------------------------------------------------------------------- LARGE_INTEGER win_file_size; if (!GetFileSizeEx(file_handle, &win_file_size)) { - Dqn_Win_LastErrorToBuffer(&error_msg); - Dqn_Log_ErrorF("Failed to query file size [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error_msg)); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("Failed to query file size [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg)); return nullptr; } @@ -587,19 +599,19 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi if (read_result == 0) { Dqn_Allocator_Dealloc(allocator, result, bytes_desired); - Dqn_Win_LastErrorToBuffer(&error_msg); - Dqn_Log_ErrorF("'ReadFile' failed to load file to memory [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error_msg)); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("'ReadFile' failed to load file to memory [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg)); return nullptr; } if (bytes_read != bytes_desired) { - Dqn_Win_LastErrorToBuffer(&error_msg); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Allocator_Dealloc(allocator, result, bytes_desired); Dqn_Log_ErrorF("'ReadFile' failed to read all bytes into file [file=%.*s, bytes_desired=%u, bytes_read=%u, reason=%.*s]", DQN_STRING_FMT(path8), bytes_desired, bytes_read, - DQN_STRING_FMT(error_msg)); + DQN_STRING_FMT(error.msg)); return nullptr; } @@ -666,7 +678,7 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 path8 = Dqn_String8_Init(path, path_size); - Dqn_String16 path16 = Dqn_Win_String8ToString16Allocator(path8, scratch.allocator); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path8); void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ GENERIC_WRITE, @@ -677,7 +689,8 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co /*HANDLE hTemplateFile*/ nullptr); if (file_handle == INVALID_HANDLE_VALUE) { - Dqn_Log_ErrorF("Failed to open file for writing [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(Dqn_Win_LastError())); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("Failed to open file for writing [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg)); return result; } DQN_DEFER { CloseHandle(file_handle); }; @@ -748,7 +761,7 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u } Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16Allocator(path, scratch.allocator); + Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ access_mode, /*DWORD dwShareMode*/ 0, @@ -758,12 +771,12 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u /*HANDLE hTemplateFile*/ nullptr); if (handle == INVALID_HANDLE_VALUE) { - Dqn_WinErrorMsg msg = Dqn_Win_LastError(); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(result.error, DQN_ARRAY_UCOUNT(result.error), "Open file failed: %.*s for \"%.*s\"", - DQN_STRING_FMT(msg), + DQN_STRING_FMT(error.msg), DQN_STRING_FMT(path)); return result; } @@ -842,12 +855,13 @@ DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize si } if (!result) { - Dqn_WinErrorMsg msg = Dqn_Win_LastError(); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); file->error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(file->error, DQN_ARRAY_UCOUNT(file->error), "Write file failed: %.*s for %.*s", - DQN_STRING_FMT(msg)); + DQN_STRING_FMT(error.msg)); } #else result = fwrite(buffer, DQN_CAST(Dqn_usize)size, 1 /*count*/, file->handle) == 1 /*count*/; @@ -917,7 +931,7 @@ DQN_API bool Dqn_FsPath_Add(Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 p return result; } -DQN_API bool Dqn_FsPath_AddF(Dqn_Arena *arena, Dqn_FsPath *fs_path, char const *fmt, ...) +DQN_API bool Dqn_FsPath_AddF(Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -957,7 +971,7 @@ DQN_API Dqn_String8 Dqn_FsPath_Convert(Dqn_Arena *arena, Dqn_String8 path) return result; } -DQN_API Dqn_String8 Dqn_FsPath_ConvertF(Dqn_Arena *arena, char const *fmt, ...) +DQN_API Dqn_String8 Dqn_FsPath_ConvertF(Dqn_Arena *arena, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); va_list args; @@ -1095,41 +1109,45 @@ DQN_API uint64_t Dqn_Date_EpochTime() #if defined(DQN_OS_WIN32) #if !defined(DQN_NO_WIN) // NOTE: [$WIND] Dqn_Win =========================================================================== -DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg) +DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena) { - msg->code = GetLastError(); - msg->data[0] = 0; + Dqn_WinError result = {}; + result.code = GetLastError(); + if (arena) { + unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + void *module_to_get_errors_from = nullptr; - unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - void *module_to_get_errors_from = nullptr; + if (result.code >= 12000 && result.code <= 12175) { // WinINET Errors + flags |= FORMAT_MESSAGE_FROM_HMODULE; + module_to_get_errors_from = GetModuleHandleA("wininet.dll"); + } - if (msg->code >= 12000 && msg->code <= 12175) { // WinINET Errors - flags |= FORMAT_MESSAGE_FROM_HMODULE; - module_to_get_errors_from = GetModuleHandleA("wininet.dll"); + int32_t size = FormatMessageA(flags, + module_to_get_errors_from, // LPCVOID lpSource, + result.code, // unsigned long dwMessageId, + 0, // unsigned long dwLanguageId, + nullptr, // LPSTR lpBuffer, + 0, // unsigned long nSize, + nullptr); // va_list * Arguments + + if (size) { + Dqn_String8 buffer = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), size, Dqn_ZeroMem_No); + int32_t buffer_size = DQN_CAST(int32_t)buffer.size; + int32_t new_size = FormatMessageA(flags, + module_to_get_errors_from, // LPCVOID lpSource, + result.code, // unsigned long dwMessageId, + 0, // unsigned long dwLanguageId, + buffer.data, // LPSTR lpBuffer, + buffer_size, // unsigned long nSize, + nullptr); // va_list * Arguments + if (DQN_CHECK(new_size == size)) { + result.msg = buffer; + } + } } - - msg->size = FormatMessageA(flags, - module_to_get_errors_from, // LPCVOID lpSource, - msg->code, // unsigned long dwMessageId, - 0, // unsigned long dwLanguageId, - msg->data, // LPSTR lpBuffer, - DQN_ARRAY_ICOUNT(msg->data), // unsigned long nSize, - nullptr); // va_list * Arguments - - if (msg->size >= 2 && - (msg->data[msg->size - 2] == '\r' && msg->data[msg->size - 1] == '\n')) { - msg->size -= 2; - } -} - -DQN_API Dqn_WinErrorMsg Dqn_Win_LastError() -{ - Dqn_WinErrorMsg result = {}; - Dqn_Win_LastErrorToBuffer(&result); return result; } - DQN_API void Dqn_Win_MakeProcessDPIAware() { typedef bool SetProcessDpiAwareProc(void); @@ -1141,6 +1159,9 @@ DQN_API void Dqn_Win_MakeProcessDPIAware() // GetProcAddress on the DPI function. If it's not there, we're on an old // version of windows, so we can call an older version of the API. void *lib_handle = LoadLibraryA("user32.dll"); + if (!lib_handle) + return; + if (auto *set_process_dpi_awareness_context = DQN_CAST(SetProcessDpiAwarenessContextProc *)GetProcAddress(DQN_CAST(HMODULE)lib_handle, "SetProcessDpiAwarenessContext")) { set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else if (auto *set_process_dpi_awareness = DQN_CAST(SetProcessDpiAwarenessProc *)GetProcAddress(DQN_CAST(HMODULE)lib_handle, "SetProcessDpiAwareness")) { @@ -1150,145 +1171,130 @@ DQN_API void Dqn_Win_MakeProcessDPIAware() } } -DQN_API void Dqn_Win_DumpLastError_(Dqn_CallSite call_site, char const *fmt, ...) -{ - // TODO(dqn): Hmmm .. should this be a separate log or part of the above - // macro. If so we need to make the logging macros more flexible. - Dqn_WinErrorMsg msg = Dqn_Win_LastError(); - if (fmt) { - va_list args; - va_start(args, fmt); - Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, call_site, fmt, args); - va_end(args); - } - - if (msg.size) - Dqn_Log_TypeFCallSite(Dqn_LogType_Error, call_site, "Last Windows error [msg=%.*s]", DQN_STRING_FMT(msg)); - else - Dqn_Log_TypeFCallSite(Dqn_LogType_Error, call_site, "Failed to dump last error, no error message found [format_message_error=%d, msg_error=%d]", GetLastError(), msg.code); -} - -// NOTE: Windows UTF8 to String16 -// ----------------------------------------------------------------------------- -DQN_API int Dqn_Win_CString8ToCString16(const char *src, int src_size, wchar_t *dest, int dest_size) -{ - int result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src, src_size, dest, dest_size); - if (result && dest && dest_size > 0) - dest[DQN_MIN(result, dest_size - 1)] = 0; // Null-terminate the buffer - return result; -} - -DQN_API int Dqn_Win_String8ToCString16(Dqn_String8 src, wchar_t *dest, int dest_size) -{ - int result = 0; - int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); - if (src_size) - result = Dqn_Win_CString8ToCString16(src.data, src_size, dest, dest_size); - return result; -} - -DQN_API Dqn_String16 Dqn_Win_String8ToString16Allocator(Dqn_String8 src, Dqn_Allocator allocator) +// NOTE: Windows UTF8 to String16 ============================================== +DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src) { Dqn_String16 result = {}; - int required = Dqn_Win_String8ToCString16(src, nullptr, 0); - if (required > 0) { - result.data = Dqn_Allocator_NewArray(allocator, wchar_t, required + 1, Dqn_ZeroMem_No); - if (result.data) { - result.size = Dqn_Win_String8ToCString16(src, result.data, required + 1); - DQN_ASSERT(result.size == DQN_CAST(Dqn_usize)required); - } + if (!arena || !Dqn_String8_IsValid(src)) + return result; + + int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, nullptr /*dest*/, 0 /*dest size*/); + if (required_size <= 0) + return result; + + wchar_t *buffer = Dqn_Arena_NewArray(arena, wchar_t, required_size + 1, Dqn_ZeroMem_No); + if (!buffer) + return result; + + int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, buffer, required_size); + if (DQN_CHECK(chars_written == required_size)) { + result.data = buffer; + result.size = chars_written; + result.data[result.size] = 0; } return result; } -// NOTE: Windows String16 To UTF8 -// ----------------------------------------------------------------------------- -DQN_API int Dqn_Win_CString16ToCString8(const wchar_t* src, int src_size, char *dest, int dest_size) -{ - int result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src, src_size, dest, dest_size, nullptr, nullptr); - if (result && dest && dest_size > 0) - dest[DQN_MIN(result, dest_size - 1)] = 0; - return result; -} - -DQN_API Dqn_String8 Dqn_Win_CString16ToString8Allocator(const wchar_t* src, int src_size, Dqn_Allocator allocator) -{ - Dqn_String8 result = {}; - int required = Dqn_Win_CString16ToCString8(src, src_size, nullptr, 0); - if (required > 0) { - // NOTE: String allocate ensures there's one extra byte for - // null-termination already so no-need to +1 the required size - result = Dqn_String8_Allocate(allocator, DQN_CAST(Dqn_usize)required, Dqn_ZeroMem_No); - if (Dqn_String8_IsValid(result)) { - int next_required = Dqn_Win_CString16ToCString8(src, src_size, result.data, required + 1); - DQN_ASSERT(required == next_required); - } - } - return result; -} - -DQN_API int Dqn_Win_String16ToCString8(Dqn_String16 src, char *dest, int dest_size) +DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String8 src, wchar_t *dest, Dqn_usize dest_size) { int result = 0; - int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); - if (src_size) { - result = Dqn_Win_CString16ToCString8(src.data, src_size, dest, dest_size); - } + if (!Dqn_String8_IsValid(src)) + return result; + + result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, nullptr /*dest*/, 0 /*dest size*/); + if (result <= 0 || result > dest_size || !dest) + return result; + + result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, dest, DQN_CAST(int)dest_size); + dest[DQN_MIN(result, dest_size - 1)] = 0; return result; } -DQN_API Dqn_String8 Dqn_Win_String16ToString8Allocator(Dqn_String16 src, Dqn_Allocator allocator) +// NOTE: Windows String16 To UTF8 ================================================================== +DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, Dqn_usize dest_size) +{ + int result = 0; + if (!Dqn_String8_IsValid(src)) + return result; + + int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); + if (src_size <= 0) + return result; + + result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr); + if (result <= 0 || result > dest_size || !dest) + return result; + + result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, dest, DQN_CAST(int)dest_size, nullptr, nullptr); + dest[DQN_MIN(result, dest_size - 1)] = 0; + return result; +} + +DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src) { Dqn_String8 result = {}; - int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); - if (src_size) { - result = Dqn_Win_CString16ToString8Allocator(src.data, src_size, allocator); - } - return result; -} - -// NOTE: Windows Executable Directory -// ----------------------------------------------------------------------------- -DQN_API Dqn_usize Dqn_Win_EXEDirW(wchar_t *buffer, Dqn_usize size) -{ - wchar_t module_path[DQN_OS_WIN32_MAX_PATH]; - Dqn_usize module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_ARRAY_UCOUNT(module_path)); - DQN_HARD_ASSERTF(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "How the hell?"); - - Dqn_usize result = 0; - for (Dqn_usize index = module_size - 1; !result && index < module_size; index--) - result = module_path[index] == '\\' ? index : 0; - - if (!buffer || size < result) { + if (!arena || !Dqn_String8_IsValid(src)) return result; + + int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); + if (src_size <= 0) + return result; + + int required_size = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr); + if (required_size <= 0) + return result; + + // NOTE: String allocate ensures there's one extra byte for + // null-termination already so no-need to +1 the required size + Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(arena); + Dqn_String8 buffer = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), required_size, Dqn_ZeroMem_No); + if (!Dqn_String8_IsValid(buffer)) + return result; + + int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DQN_CAST(int)buffer.size, nullptr, nullptr); + if (DQN_CHECK(chars_written == required_size)) { + result = buffer; + result.data[result.size] = 0; + } else { + // NOTE: Revert the temp memory because we encountered an error + Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/); } - DQN_MEMCPY(buffer, module_path, sizeof(wchar_t) * result); return result; } -DQN_API Dqn_String16 Dqn_Win_EXEDirWArena(Dqn_Arena *arena) +// NOTE: Windows Executable Directory ========================================== +DQN_API Dqn_String16 Dqn_Win_EXEDirW(Dqn_Arena *arena) { - wchar_t dir[DQN_OS_WIN32_MAX_PATH]; - Dqn_usize dir_size = Dqn_Win_EXEDirW(dir, DQN_ARRAY_ICOUNT(dir)); - DQN_HARD_ASSERTF(dir_size <= DQN_ARRAY_ICOUNT(dir), "How the hell?"); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); + Dqn_String16 result = {}; + Dqn_usize module_size = 0; + wchar_t *module_path = nullptr; + do { + module_size += 256; + module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No); + if (!module_path) + return result; + module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size); + } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER); - Dqn_String16 result = {}; - if (dir_size > 0) { - result.data = Dqn_Arena_NewArrayCopyZ(arena, wchar_t, dir, dir_size); - if (result.data) { - result.size = dir_size; - } - } + Dqn_usize index_of_last_slash = 0; + for (Dqn_usize index = module_size - 1; !index_of_last_slash && index < module_size; index--) + index_of_last_slash = module_path[index] == '\\' ? index : 0; + + result.data = Dqn_Arena_NewArray(arena, wchar_t, index_of_last_slash + 1, Dqn_ZeroMem_No); + result.size = index_of_last_slash; + DQN_MEMCPY(result.data, module_path, sizeof(wchar_t) * result.size); + result.data[result.size] = 0; return result; } DQN_API Dqn_String8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_String8 suffix) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); - Dqn_String16 suffix16 = Dqn_Win_String8ToString16Allocator(suffix, Dqn_Arena_Allocator(scratch.arena)); + Dqn_String16 suffix16 = Dqn_Win_String8ToString16(scratch.arena, suffix); Dqn_String16 dir16 = Dqn_Win_WorkingDirW(Dqn_Arena_Allocator(scratch.arena), suffix16); - Dqn_String8 result = Dqn_Win_String16ToString8Allocator(dir16, allocator); + Dqn_String8 result = Dqn_Win_String16ToString8(scratch.arena, dir16); return result; } @@ -1367,11 +1373,9 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) if (!Dqn_String8_IsValid(path) || !it || path.size <= 0) return false; - wchar_t path16[DQN_OS_WIN32_MAX_PATH + 1]; - path16[0] = 0; - Dqn_usize path16_size = 0; - + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Win_FolderIteratorW wide_it = {}; + Dqn_String16 path16 = {}; if (it->handle) { wide_it.handle = it->handle; } else { @@ -1380,34 +1384,26 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) bool has_glob = Dqn_String8_EndsWith(path, DQN_STRING8("\\*")) || Dqn_String8_EndsWith(path, DQN_STRING8("/*")); - path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_UCOUNT(path16)); - if (path16_size <= 0) // Conversion error - return false; - + Dqn_String8 adjusted_path = path; if (!has_glob) { // NOTE: We are missing the glob for enumerating the files, we will // add those characters in this branch, so overwrite the null // character, add the glob and re-null terminate the buffer. - Dqn_usize space = DQN_OS_WIN32_MAX_PATH - path16_size; - if (needs_asterisks) { - if (space < 2) - return false; - path16[path16_size++] = L'*'; - } else { - if (space < 3) - return false; - path16[path16_size++] = L'\\'; - path16[path16_size++] = L'*'; - } - path16[path16_size++] = 0; + if (needs_asterisks) + adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s*", DQN_STRING_FMT(path)); + else + adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/*", DQN_STRING_FMT(path)); } - DQN_ASSERT(path16_size <= DQN_OS_WIN32_MAX_PATH); + + path16 = Dqn_Win_String8ToString16(scratch.arena, adjusted_path); + if (path16.size <= 0) // Conversion error + return false; } - bool result = Dqn_Win_FolderWIterate(Dqn_String16{path16, path16_size}, &wide_it); - it->handle = wide_it.handle; + bool result = Dqn_Win_FolderWIterate(path16, &wide_it); + it->handle = wide_it.handle; if (result) { - int size = Dqn_Win_String16ToCString8(wide_it.file_name, it->file_name_buf, DQN_ARRAY_ICOUNT(it->file_name_buf)); + int size = Dqn_Win_String16ToString8Buffer(wide_it.file_name, it->file_name_buf, DQN_ARRAY_UCOUNT(it->file_name_buf)); it->file_name = Dqn_String8_Init(it->file_name_buf, size); } @@ -1427,7 +1423,14 @@ DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_s // Seperate the URL into bits and bobs Dqn_WinNetHandle result = {}; if (!InternetCrackUrlA(url, url_size, 0 /*flags*/, &components)) { - Dqn_Log_ErrorF("InternetCrackUrlA failed [reason=%.*s]", DQN_STRING_FMT(Dqn_Win_LastError())); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); + Dqn_Log_ErrorF("InternetCrackUrlA failed [reason=%.*s]", DQN_STRING_FMT(error.msg)); + return result; + } + + if (components.lpszHostName == nullptr) { + Dqn_Log_ErrorF("Windows returnd a null host-name after trying to crack the URL.\n\nURL: %.*s", url_size, url); return result; } @@ -1602,10 +1605,12 @@ DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle * if (!HttpSendRequestA(handle->http_handle, nullptr /*headers*/, 0 /*headers length*/, (char *)post_data, post_data_size)) { handle->state = Dqn_WinNetHandleState_RequestFailed; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_ErrorF("Failed to send request to %.*s [reason=%.*s]", handle->host_name_size, handle->host_name, - DQN_STRING_FMT(Dqn_Win_LastError())); + DQN_STRING_FMT(error.msg)); return result; } @@ -1864,36 +1869,33 @@ DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size) } #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) -DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Allocator allocator) +DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Arena *arena) { Dqn_String8 result = {}; + if (!arena) + return result; #if defined(DQN_OS_WIN32) - wchar_t exe_dir[DQN_OS_WIN32_MAX_PATH]; - Dqn_usize exe_dir_size = Dqn_Win_EXEDirW(exe_dir, DQN_ARRAY_ICOUNT(exe_dir)); - result = Dqn_Win_CString16ToString8Allocator(exe_dir, DQN_CAST(int)exe_dir_size, allocator); + + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); + Dqn_String16 exe_dir16 = Dqn_Win_EXEDirW(scratch.arena); + result = Dqn_Win_String16ToString8(arena, exe_dir16); + #elif defined(DQN_OS_UNIX) + int required_size_wo_null_terminator = 0; - for (int try_size = 128; - ; - try_size *= 2) - { + for (int try_size = 128;; try_size *= 2) { auto scoped_arena = Dqn_ArenaTempMemoryScope(arena); char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No); int bytes_written = readlink("/proc/self/exe", try_buf, try_size); - if (bytes_written == -1) - { + if (bytes_written == -1) { // Failed, we're unable to determine the executable directory break; - } - else if (bytes_written == try_size) - { + } else if (bytes_written == try_size) { // Try again, if returned size was equal- we may of prematurely // truncated according to the man pages continue; - } - else - { + } else { // readlink will give us the path to the executable. Once we // determine the correct buffer size required to get the full file // path, we do some post-processing on said string and extract just @@ -1906,12 +1908,8 @@ DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Allocator allocator) DQN_ASSERTF(bytes_written < try_size, "bytes_written can never be greater than the try size, function writes at most try_size"); required_size_wo_null_terminator = bytes_written; - for (int index_of_last_slash = bytes_written; - index_of_last_slash >= 0; - index_of_last_slash--) - { - if (try_buf[index_of_last_slash] == '/') - { + for (Dqn_isize index_of_last_slash = bytes_written; index_of_last_slash >= 0; index_of_last_slash--) { + if (try_buf[index_of_last_slash] == '/') { // NOTE: We take the index of the last slash and not // (index_of_last_slash + 1) because we want to exclude the // trailing backslash as a convention of this library. @@ -1919,27 +1917,22 @@ DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Allocator allocator) break; } } - break; } } - if (required_size_wo_null_terminator) - { + if (required_size_wo_null_terminator) { Dqn_ArenaTempMemory scope = Dqn_Arena_BeginTempMemory(arena); char *exe_path = Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No); exe_path[required_size_wo_null_terminator] = 0; int bytes_written = readlink("/proc/self/exe", exe_path, required_size_wo_null_terminator); - if (bytes_written == -1) - { + if (bytes_written == -1) { // Note that if read-link fails again can be because there's // a potential race condition here, our exe or directory could have // been deleted since the last call, so we need to be careful. Dqn_Arena_EndTempMemory(scope); - } - else - { + } else { result = Dqn_String8_Init(exe_path, required_size_wo_null_terminator); } } diff --git a/dqn_platform.h b/dqn_platform.h index ba94cd4..24f8fc5 100644 --- a/dqn_platform.h +++ b/dqn_platform.h @@ -48,20 +48,20 @@ DQN_API Dqn_PrintStyle Dqn_Print_StyleBold (); // NOTE: Print ===================================================================================== DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string); -DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, char const *fmt, ...); -DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, char const *fmt, va_list args); +DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); -DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...); -DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args); +DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string); -DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, char const *fmt, ...); -DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, char const *fmt, va_list args); +DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); -DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...); -DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args); +DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); // NOTE: ANSI Formatting Codes ===================================================================== Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b); @@ -225,11 +225,11 @@ struct Dqn_FsPath DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path); DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path); -DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, char const *fmt, ...); +DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path); DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator); DQN_API Dqn_String8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_String8 path); -DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, char const *fmt, ...); +DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); #define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/")) #define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\")) @@ -285,15 +285,13 @@ DQN_API uint64_t Dqn_Date_EpochTime (); // DPI aware on Windows and ensure that application UI is scaled up // appropriately for the monitor. -struct Dqn_WinErrorMsg +struct Dqn_WinError { unsigned long code; - char data[DQN_KILOBYTES(64) - 1]; // Maximum error size - unsigned long size; + Dqn_String8 msg; }; -DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg); -DQN_API Dqn_WinErrorMsg Dqn_Win_LastError(); -DQN_API void Dqn_Win_MakeProcessDPIAware(); +DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena); +DQN_API void Dqn_Win_MakeProcessDPIAware(); // NOTE: Windows String8 <-> String16 =========================================== // Convert a UTF8 <-> UTF16 string. @@ -308,16 +306,12 @@ DQN_API void Dqn_Win_MakeProcessDPIAware(); // written/required for conversion. 0 if there was a conversion error and can be // queried using 'Dqn_Win_LastError' -DQN_API int Dqn_Win_CString8ToCString16 (char const *src, int src_size, wchar_t *dest, int dest_size); -DQN_API int Dqn_Win_String8ToCString16 (Dqn_String8 src, wchar_t *dest, int dest_size); -DQN_API Dqn_String16 Dqn_Win_String8ToString16Allocator (Dqn_String8 src, Dqn_Allocator allocator); +DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src); +DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String16 src, char *dest, Dqn_usize dest_size); +DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src); +DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, Dqn_usize dest_size); -DQN_API int Dqn_Win_CString16ToCString8 (wchar_t const *src, int src_size, char *dest, int dest_size); -DQN_API Dqn_String8 Dqn_Win_CString16ToString8Allocator(wchar_t const *src, int src_size, Dqn_Allocator allocator); -DQN_API int Dqn_Win_String16ToCString8 (Dqn_String16 src, char *dest, int dest_size); -DQN_API Dqn_String8 Dqn_Win_String16ToString8Allocator (Dqn_String16 src, Dqn_Allocator allocator); - -// NOTE: Path navigatoin =========================================================================== +// NOTE: Path navigation =========================================================================== // NOTE: API ======================================================================================= // @proc Dqn_Win_EXEDirW, Dqn_Win_EXEDirWArena // @desc Evaluate the current executable's directory that is running when this @@ -354,8 +348,7 @@ struct Dqn_Win_FolderIterator char file_name_buf[512]; }; -DQN_API Dqn_usize Dqn_Win_EXEDirW (wchar_t *buffer, Dqn_usize size); -DQN_API Dqn_String16 Dqn_Win_EXEDirWArena (Dqn_Arena *arena); +DQN_API Dqn_String16 Dqn_Win_EXEDirW (Dqn_Arena *arena); DQN_API Dqn_String8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_String8 suffix); DQN_API Dqn_String16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_String16 suffix); DQN_API bool Dqn_Win_FolderIterate (Dqn_String8 path, Dqn_Win_FolderIterator *it); @@ -503,7 +496,7 @@ struct Dqn_OSTimer DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size); #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) -DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Allocator allocator); +DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Arena* arena); #endif DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds); DQN_API uint64_t Dqn_OS_PerfCounterNow (); diff --git a/dqn_strings.cpp b/dqn_strings.cpp index 8487bb8..7974e85 100644 --- a/dqn_strings.cpp +++ b/dqn_strings.cpp @@ -1,5 +1,5 @@ // NOTE: [$CSTR] Dqn_CString8 ====================================================================== -DQN_API Dqn_usize Dqn_CString8_FSize(char const *fmt, ...) +DQN_API Dqn_usize Dqn_CString8_FSize(DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -8,7 +8,7 @@ DQN_API Dqn_usize Dqn_CString8_FSize(char const *fmt, ...) return result; } -DQN_API Dqn_usize Dqn_CString8_FVSize(char const *fmt, va_list args) +DQN_API Dqn_usize Dqn_CString8_FVSize(DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { va_list args_copy; va_copy(args_copy, args); @@ -47,12 +47,6 @@ DQN_API Dqn_String8 Dqn_String8_InitCString8(char const *src) return result; } -DQN_API bool Dqn_String8_IsValid(Dqn_String8 string) -{ - bool result = string.data; - return result; -} - DQN_API bool Dqn_String8_IsAll(Dqn_String8 string, Dqn_String8IsAll is_all) { bool result = Dqn_String8_IsValid(string); @@ -364,14 +358,17 @@ DQN_API Dqn_String8 Dqn_String8_FileNameFromPath(Dqn_String8 path) if (!Dqn_String8_IsValid(result)) return result; - for (Dqn_usize i = result.size - 1; i < result.size; --i) { - if (result.data[i] == '\\' || result.data[i] == '/') { + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop. + for (Dqn_usize index = result.size - 1; index < result.size; --index) { + if (result.data[index] == '\\' || result.data[index] == '/') { char const *end = result.data + result.size; - result.data = result.data + (i + 1); + result.data = result.data + (index + 1); result.size = end - result.data; break; } } + DQN_MSVC_WARNING_POP return result; } @@ -548,7 +545,7 @@ DQN_API bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs) } #endif -DQN_API Dqn_String8 Dqn_String8_InitF(Dqn_Allocator allocator, char const *fmt, ...) +DQN_API Dqn_String8 Dqn_String8_InitF(Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list va; va_start(va, fmt); @@ -557,7 +554,7 @@ DQN_API Dqn_String8 Dqn_String8_InitF(Dqn_Allocator allocator, char const *fmt, return result; } -DQN_API Dqn_String8 Dqn_String8_InitFV(Dqn_Allocator allocator, char const *fmt, va_list args) +DQN_API Dqn_String8 Dqn_String8_InitFV(Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { Dqn_String8 result = {}; if (!fmt) @@ -632,7 +629,7 @@ DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_Stri return result; } -DQN_API bool Dqn_String8Builder_AppendFV(Dqn_String8Builder *builder, char const *fmt, va_list args) +DQN_API bool Dqn_String8Builder_AppendFV(Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { Dqn_String8 string = Dqn_String8_InitFV(builder->allocator, fmt, args); if (string.size == 0) @@ -644,7 +641,7 @@ DQN_API bool Dqn_String8Builder_AppendFV(Dqn_String8Builder *builder, char const return result; } -DQN_API bool Dqn_String8Builder_AppendF(Dqn_String8Builder *builder, char const *fmt, ...) +DQN_API bool Dqn_String8Builder_AppendF(Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -718,17 +715,18 @@ DQN_API uint8_t Dqn_Char_HexToU8(char ch) return result; } -char constexpr DQN_HEX_LUT[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +static char constexpr DQN_HEX_LUT[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; DQN_API char Dqn_Char_ToHex(char ch) { char result = DQN_CAST(char)-1; - if (ch <= 16) result = DQN_HEX_LUT[DQN_CAST(unsigned)ch]; + if (ch < 16) + result = DQN_HEX_LUT[ch]; return result; } DQN_API char Dqn_Char_ToHexUnchecked(char ch) { - char result = DQN_HEX_LUT[DQN_CAST(unsigned)ch]; + char result = DQN_HEX_LUT[ch]; return result; } diff --git a/dqn_strings.h b/dqn_strings.h index fbb54fc..27ab46a 100644 --- a/dqn_strings.h +++ b/dqn_strings.h @@ -17,8 +17,8 @@ DQN_API template constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; } DQN_API template constexpr Dqn_usize Dqn_CString8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; } -DQN_API Dqn_usize Dqn_CString8_FSize (char const *fmt, ...); -DQN_API Dqn_usize Dqn_CString8_FVSize (char const *fmt, va_list args); +DQN_API Dqn_usize Dqn_CString8_FSize (DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API Dqn_usize Dqn_CString8_FVSize (DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API Dqn_usize Dqn_CString8_Size (char const *a); DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a); @@ -308,11 +308,11 @@ struct Dqn_String8ToI64Result }; DQN_API Dqn_String8 Dqn_String8_InitCString8 (char const *src); -DQN_API bool Dqn_String8_IsValid (Dqn_String8 string); +#define Dqn_String8_IsValid(string) ((string).data) DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all); -DQN_API Dqn_String8 Dqn_String8_InitF (Dqn_Allocator allocator, char const *fmt, ...); -DQN_API Dqn_String8 Dqn_String8_InitFV (Dqn_Allocator allocator, char const *fmt, va_list args); +DQN_API Dqn_String8 Dqn_String8_InitF (Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API Dqn_String8 Dqn_String8_InitFV (Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API Dqn_String8 Dqn_String8_Allocate (Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem); DQN_API Dqn_String8 Dqn_String8_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size); DQN_API Dqn_String8 Dqn_String8_Copy (Dqn_Allocator allocator, Dqn_String8 string); @@ -438,11 +438,11 @@ template struct Dqn_FString8 char const *end () const { return data + size; } }; -template Dqn_FString8 Dqn_FString8_InitF (char const *fmt, ...); +template Dqn_FString8 Dqn_FString8_InitF (DQN_FMT_STRING_ANNOTATE char const *fmt, ...); template Dqn_usize Dqn_FString8_Max (Dqn_FString8 const *string); template void Dqn_FString8_Clear (Dqn_FString8 *string); -template bool Dqn_FString8_AppendFV (Dqn_FString8 *string, char const *fmt, va_list va); -template bool Dqn_FString8_AppendF (Dqn_FString8 *string, char const *fmt, ...); +template bool Dqn_FString8_AppendFV (Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list va); +template bool Dqn_FString8_AppendF (Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); template bool Dqn_FString8_AppendCString8 (Dqn_FString8 *string, char const *value, Dqn_usize size); template bool Dqn_FString8_Append (Dqn_FString8 *string, Dqn_String8 value); template Dqn_String8 Dqn_FString8_ToString8 (Dqn_FString8 const *string); @@ -489,8 +489,8 @@ struct Dqn_String8Builder Dqn_usize count; ///< The number of links in the linked list of strings }; -DQN_API bool Dqn_String8Builder_AppendF (Dqn_String8Builder *builder, char const *fmt, ...); -DQN_API bool Dqn_String8Builder_AppendFV (Dqn_String8Builder *builder, char const *fmt, va_list args); +DQN_API bool Dqn_String8Builder_AppendF (Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API bool Dqn_String8Builder_AppendFV (Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); DQN_API bool Dqn_String8Builder_AppendRef (Dqn_String8Builder *builder, Dqn_String8 string); DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_String8 string); DQN_API Dqn_String8 Dqn_String8Builder_Build (Dqn_String8Builder const *builder, Dqn_Allocator allocator); @@ -512,7 +512,7 @@ DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint); #if !defined(DQN_NO_FSTRING8) // NOTE: [$FSTR] Dqn_FString8 ====================================================================== -template Dqn_FString8 Dqn_FString8_InitF(char const *fmt, ...) +template Dqn_FString8 Dqn_FString8_InitF(DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { Dqn_FString8 result = {}; if (fmt) { @@ -535,7 +535,7 @@ template void Dqn_FString8_Clear(Dqn_FString8 *string) *string = {}; } -template bool Dqn_FString8_AppendFV(Dqn_FString8 *string, char const *fmt, va_list args) +template bool Dqn_FString8_AppendFV(Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) { bool result = false; if (!string || !fmt) @@ -553,7 +553,7 @@ template bool Dqn_FString8_AppendFV(Dqn_FString8 *string, char return result; } -template bool Dqn_FString8_AppendF(Dqn_FString8 *string, char const *fmt, ...) +template bool Dqn_FString8_AppendF(Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) { bool result = false; if (!string || !fmt)