Compare commits

..

2 Commits

Author SHA1 Message Date
2868993ebc dqn: Add MSVC SAL annotations 2023-08-25 23:42:09 +10:00
8f129e7893 dqn: Upgrade to latest lib 2023-08-25 20:35:04 +10:00
16 changed files with 490 additions and 436 deletions

View File

@ -479,7 +479,7 @@ Dqn_UTest TestDSMap()
uint64_t value = 0; uint64_t value = 0;
uint64_t grow_threshold = map_start_size * 3 / 4; uint64_t grow_threshold = map_start_size * 3 / 4;
for (; map.occupied != grow_threshold; value++) { for (; map.occupied != grow_threshold; value++) {
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1); uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key)); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key));
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_FindSlot<uint64_t>(&map, key)); DQN_UTEST_ASSERT(&test, !Dqn_DSMap_FindSlot<uint64_t>(&map, key));
@ -498,7 +498,7 @@ Dqn_UTest TestDSMap()
DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value); DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value);
{ // NOTE: One more item should cause the table to grow by 2x { // NOTE: One more item should cause the table to grow by 2x
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1); uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
bool found = false; bool found = false;
if (test_type == DSMapTestType_Set) { if (test_type == DSMapTestType_Set) {
@ -548,7 +548,7 @@ Dqn_UTest TestDSMap()
uint64_t value = 0; uint64_t value = 0;
uint64_t shrink_threshold = map.size * 1 / 4; uint64_t shrink_threshold = map.size * 1 / 4;
for (; map.occupied != shrink_threshold; value++) { for (; map.occupied != shrink_threshold; value++) {
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1); uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key)); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key));
@ -561,7 +561,7 @@ Dqn_UTest TestDSMap()
DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value); DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value);
{ // NOTE: One more item should cause the table to grow by 2x { // NOTE: One more item should cause the table to grow by 2x
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1); uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
Dqn_DSMap_Erase(&map, key); Dqn_DSMap_Erase(&map, key);
value++; value++;
@ -600,7 +600,7 @@ Dqn_UTest TestDSMap()
} }
for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1); uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key)); DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key));
Dqn_DSMap_Erase(&map, key); Dqn_DSMap_Erase(&map, key);
@ -746,6 +746,18 @@ Dqn_UTest TestIntrinsics()
// atomics/intrinsics are implemented using macros we ensure the macro was // atomics/intrinsics are implemented using macros we ensure the macro was
// written properly with these tests. // 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_GROUP(test, "Dqn_Atomic") {
DQN_UTEST_TEST("Dqn_Atomic_AddU32") { DQN_UTEST_TEST("Dqn_Atomic_AddU32") {
uint32_t val = 0; uint32_t val = 0;
@ -775,7 +787,7 @@ Dqn_UTest TestIntrinsics()
long a = 0; long a = 0;
long b = 111; long b = 111;
Dqn_Atomic_SetValue32(&a, b); 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") { DQN_UTEST_TEST("Dqn_Atomic_SetValue64") {
@ -797,6 +809,7 @@ Dqn_UTest TestIntrinsics()
Dqn_CompilerWriteBarrierAndCPUWriteFence; Dqn_CompilerWriteBarrierAndCPUWriteFence;
Dqn_UTest_End(&test); Dqn_UTest_End(&test);
} }
DQN_MSVC_WARNING_POP
return test; return test;
} }
@ -1067,7 +1080,7 @@ Dqn_UTest TestOS()
DQN_UTEST_TEST("Query executable directory") { DQN_UTEST_TEST("Query executable directory") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); 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_ASSERT(&test, Dqn_String8_IsValid(result));
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STRING_FMT(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_GROUP(test, "Dqn_String8") {
DQN_UTEST_TEST("Initialise with string literal w/ macro") { DQN_UTEST_TEST("Initialise with string literal w/ macro") {
Dqn_String8 string = DQN_STRING8("AB"); 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[0] == 'A', "string[0]: %c", string.data[0]);
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
} }
@ -1241,7 +1254,7 @@ Dqn_UTest TestString8()
DQN_UTEST_TEST("Initialise with format string") { DQN_UTEST_TEST("Initialise with format string") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 string = Dqn_String8_InitF(scratch.allocator, "%s", "AB"); 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[0] == 'A', "string[0]: %c", string.data[0]);
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
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]);
@ -1251,7 +1264,7 @@ Dqn_UTest TestString8()
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 string = DQN_STRING8("AB"); Dqn_String8 string = DQN_STRING8("AB");
Dqn_String8 copy = Dqn_String8_Copy(scratch.allocator, string); 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[0] == 'A', "copy[0]: %c", copy.data[0]);
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]); DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
DQN_UTEST_ASSERTF(&test, copy.data[2] == 0, "copy[2]: %c", copy.data[2]); 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_UTEST_TEST("Allocate string from arena") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 string = Dqn_String8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No); 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] // NOTE: Dqn_CString8_Trim[Prefix/Suffix]
@ -1652,15 +1665,12 @@ Dqn_UTest TestVArray()
// from the memory block (and hence the array) contiguously // from the memory block (and hence the array) contiguously
// when the size of the object is not aligned with the required // when the size of the object is not aligned with the required
// alignment of the object. // alignment of the object.
DQN_MSVC_WARNING_PUSH
#if defined(_MSC_VER) DQN_MSVC_WARNING_DISABLE(4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier
#pragma warning(push)
#pragma warning(disable: 4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier
struct alignas(8) UnalignedObject { struct alignas(8) UnalignedObject {
char data[511]; char data[511];
}; };
#pragma warning(pop) DQN_MSVC_WARNING_POP
#endif // _MSC_VER
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(scratch.arena, DQN_KILOBYTES(64)); Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(scratch.arena, DQN_KILOBYTES(64));
@ -1699,35 +1709,35 @@ Dqn_UTest TestWin()
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Win") { DQN_UTEST_GROUP(test, "Dqn_Win") {
DQN_UTEST_TEST("String8 to String16 size required") { 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_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
} }
DQN_UTEST_TEST("String16 to String8 size required") { 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_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
} }
DQN_UTEST_TEST("String8 to String16 size required") { 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_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
} }
DQN_UTEST_TEST("String16 to String8 size required") { 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_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
} }
DQN_UTEST_TEST("String8 to String16") { DQN_UTEST_TEST("String8 to String16") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 const INPUT = DQN_STRING8("String"); 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); 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 // Fill the string with error sentinels, which ensures the string is zero terminated
DQN_MEMSET(string, 'Z', size_required + 1); 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}; 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); 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_UTEST_TEST("String16 to String8: No null-terminate") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String16 INPUT = DQN_STRING16(L"String"); 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); 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 // Fill the string with error sentinels, which ensures the string is zero terminated
DQN_MEMSET(string, 'Z', size_required + 1); 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}; 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_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_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_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; return test;
} }

View File

@ -5,8 +5,7 @@ set code_dir=%~dp0
if not exist Build mkdir Build if not exist Build mkdir Build
pushd Build pushd Build
REM Flags REM Flags ==================================================================
REM ------------------------------------------------------------------------
REM MT Static CRT REM MT Static CRT
REM EHa- Disable exception handling REM EHa- Disable exception handling
REM GR- Disable C RTTI REM GR- Disable C RTTI
@ -15,24 +14,22 @@ pushd Build
REM Z7 Combine multi-debug files to one debug file REM Z7 Combine multi-debug files to one debug file
REM wd4201 Nonstandard extension used: nameless struct/union REM wd4201 Nonstandard extension used: nameless struct/union
REM Tp Treat header file as CPP source file 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 linker_flags=-link -nologo
set msvc_flags=-fsanitize=address set msvc_flags=-fsanitize=address
set clang_flags=-fsanitize=address,undefined set clang_flags=-fsanitize=address,undefined
REM Compiler: MSVC cl REM Compiler: MSVC cl ======================================================
REM ------------------------------------------------------------------------
where /q cl || ( where /q cl || (
echo [ERROR] cl is not found, please put MSVC's cl on the path echo [ERROR] cl is not found, please put MSVC's cl on the path
exit /b 1 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 Compiler: clang-cl =====================================================
REM ------------------------------------------------------------------------
where /q 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 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 clang-cl -D DQN_TEST_WITH_MAIN %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_clang -link
popd popd

View File

@ -48,6 +48,18 @@
#endif #endif
#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 ============================================================================ // NOTE: [$MACR] Macros ============================================================================
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++) #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++) #define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)

View File

@ -376,12 +376,12 @@ template <typename T> struct Dqn_List
}; };
// NOTE: API ======================================================================================= // NOTE: API =======================================================================================
DQN_API template <typename T> Dqn_List<T> Dqn_List_InitWithArena(Dqn_Arena *arena, Dqn_usize chunk_size = 128); template <typename T> Dqn_List<T> Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size);
DQN_API template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk); template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk);
DQN_API template <typename T> bool Dqn_List_Iterate (Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index); template <typename T> bool Dqn_List_Iterate(Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index);
DQN_API template <typename T> T * Dqn_List_Make (Dqn_List<T> *list, Dqn_usize count); template <typename T> T * Dqn_List_Make (Dqn_List<T> *list, Dqn_usize count);
DQN_API template <typename T> T * Dqn_List_Add (Dqn_List<T> *list, Dqn_usize count); template <typename T> T * Dqn_List_Add (Dqn_List<T> *list, T const &value);
#endif // !defined(DQN_NO_LIST) #endif // !defined(DQN_NO_LIST)
#if !defined(DQN_NO_VARRAY) #if !defined(DQN_NO_VARRAY)

View File

@ -208,15 +208,15 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
"%.*s" // reset "%.*s" // reset
" %.*s" // file name " %.*s" // file name
":%05u ", // line number ":%05u ", // line number
time.date_size - 2, time.date + 2, DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date
time.hms_size, time.hms, DQN_CAST(uint32_t)time.hms_size, time.hms, // hms
colour_esc.size, colour_esc.data, DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour
bold_esc.size, bold_esc.data, DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold
type.size, type.data, DQN_CAST(uint32_t)type.size, type.data, // type
type_padding, "", DQN_CAST(uint32_t)type_padding, "", // type padding
reset_esc.size, reset_esc.data, DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset
file_name.size, file_name.data, DQN_CAST(uint32_t)file_name.size, file_name.data, // file name
call_site.line); call_site.line); // line number
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size; 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)log_type;
(void)user_data; (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); Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
if (g_dqn_library->log_to_file && !g_dqn_library->log_file) { if (g_dqn_library->log_to_file && !g_dqn_library->log_file) {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) #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 #else
Dqn_String8 exe_dir = DQN_STRING8("."); Dqn_String8 exe_dir = DQN_STRING8(".");
#endif #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); 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_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator, Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
!g_dqn_library->log_no_colour, !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, fmt,
args); args);
// NOTE: Print log // NOTE: Print log =========================================================
// =========================================================================
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line); Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex); 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)); 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); Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);

View File

@ -1,6 +1,11 @@
// NOTE: [$BSTK] b_stacktrace ====================================================================== // 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 #define B_STACKTRACE_IMPL
#include "b_stacktrace.h" #include "b_stacktrace.h"
DQN_MSVC_WARNING_POP
// NOTE: [$STBS] stb_sprintf ======================================================================= // NOTE: [$STBS] stb_sprintf =======================================================================
#if !defined(DQN_STB_SPRINTF_HEADER_ONLY) #if !defined(DQN_STB_SPRINTF_HEADER_ONLY)

View File

@ -5,6 +5,7 @@
#include <Windows.h> #include <Windows.h>
#include <shellscalingapi.h> #include <shellscalingapi.h>
#endif #endif
#define B_STACKTRACE_API static #define B_STACKTRACE_API static
#include "b_stacktrace.h" #include "b_stacktrace.h"
@ -17,13 +18,14 @@
#include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc #include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc #include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
#if !defined(DQN_NO_WINNET) #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 <wininet.h> // Dqn_Win_Net -> InternetConnect ... etc #include <wininet.h> // Dqn_Win_Net -> InternetConnect ... etc
DQN_MSVC_WARNING_POP
#endif // DQN_NO_WINNET #endif // DQN_NO_WINNET
#elif !defined(_INC_WINDOWS) #elif !defined(_INC_WINDOWS)
#if defined(DQN_COMPILER_W32_MSVC) DQN_MSVC_WARNING_PUSH
#pragma warning(push) DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
#endif
#define MAX_PATH 260 #define MAX_PATH 260
// NOTE: Wait/Synchronization // 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); /*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); /*BOOL*/ int __stdcall ShowWindow (void *hWnd, int nCmdShow);
} }
#if defined(DQN_COMPILER_W32_MSVC) DQN_MSVC_WARNING_POP
#pragma warning(pop)
#endif
#endif // !defined(_INC_WINDOWS) #endif // !defined(_INC_WINDOWS)
#elif defined(DQN_OS_UNIX) #elif defined(DQN_OS_UNIX)
#include <errno.h> // errno #include <errno.h> // errno

View File

@ -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 // dividing by 10, so we write it in, then reverse it out after all is
// done. // done.
Dqn_U64String temp = {}; Dqn_U64String temp = {};
for (size_t digit_count = 0; val > 0; digit_count++) { for (Dqn_usize digit_count = 0; val > 0; digit_count++) {
if (separator && (digit_count != 0) && (digit_count % 3 == 0)) { if (separator && (digit_count != 0) && (digit_count % 3 == 0))
temp.data[temp.size++] = separator; temp.data[temp.size++] = separator;
}
auto digit = DQN_CAST(char)(val % 10); auto digit = DQN_CAST(char)(val % 10);
temp.data[temp.size++] = '0' + digit; 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 // 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]; char ch = temp.data[temp_index];
result.data[result.size++] = ch; result.data[result.size++] = ch;
} }
DQN_MSVC_WARNING_POP
} }
return result; return result;

View File

@ -309,12 +309,24 @@ DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2 rhs)
return result; return result;
} }
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_f32 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
return result;
}
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2 rhs) DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
return result; return result;
} }
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_f32 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
return result;
}
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2 rhs) DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);

View File

@ -1,7 +1,5 @@
#if defined(DQN_COMPILER_W32_MSVC) DQN_MSVC_WARNING_PUSH
#pragma warning(push) DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
#endif
#if !defined(DQN_NO_V2) #if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 =========================================================================== // NOTE: [$VEC2] Vector2 ===========================================================================
@ -88,7 +86,9 @@ DQN_API bool operator<=(Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API bool operator< (Dqn_V2 lhs, Dqn_V2 rhs); DQN_API bool operator< (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API bool operator> (Dqn_V2 lhs, Dqn_V2 rhs); DQN_API bool operator> (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2 rhs); DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2 rhs); DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2 rhs); DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_f32 rhs); DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, int32_t rhs); DQN_API Dqn_V2 operator* (Dqn_V2 lhs, int32_t rhs);
@ -157,11 +157,6 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a);
#if !defined(DQN_NO_V4) #if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 =========================================================================== // 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 union Dqn_V4
{ {
struct { Dqn_f32 x, y, z, w; }; struct { Dqn_f32 x, y, z, w; };
@ -192,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, int32_t rhs);
Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 rhs); Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 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) #endif // !defined(DQN_NO_V4)
#if !defined(DQN_NO_M4) #if !defined(DQN_NO_M4)
@ -306,7 +297,4 @@ DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutC
// NOTE: [$MATH] Other ============================================================================= // NOTE: [$MATH] Other =============================================================================
DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); 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); DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b);
DQN_MSVC_WARNING_POP
#if defined(DQN_COMPILER_W32_MSVC)
#pragma warning(pop)
#endif

View File

@ -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) DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size)
{ {
#if defined(DQN_OS_WIN32) #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); VirtualFree(ptr, size, MEM_DECOMMIT);
DQN_MSVC_WARNING_POP
#elif defined(DQN_OS_UNIX) #elif defined(DQN_OS_UNIX)
mprotect(ptr, size, PROT_NONE); mprotect(ptr, size, PROT_NONE);
madvise(ptr, size, MADV_FREE); 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) #if defined(DQN_NO_WIN)
DQN_ASSERTF(result, "VirtualProtect failed"); DQN_ASSERTF(result, "VirtualProtect failed");
#else #else
Dqn_WinErrorMsg error = Dqn_Win_LastError(); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, error.size, error.data); Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, DQN_STRING_FMT(error.msg));
#endif #endif
} }
#else #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 (!arena->curr) {
if (!Dqn_Arena_Grow(arena, size, size /*commit*/, 0 /*flags*/)) if (!Dqn_Arena_Grow(arena, size, size /*commit*/, 0 /*flags*/))
return result; return result;
DQN_ASSERT(arena->curr);
} }
result = Dqn_MemBlock_Alloc(arena->curr, size, align, zero_mem); 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; arena->tail->next = nullptr;
// NOTE: Reset the usage of all the blocks between the tail and current block's // 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; block->used = 0;
} }

View File

@ -242,9 +242,11 @@ enum Dqn_ArenaCommit
// NOTE: Allocation ================================================================================ // NOTE: Allocation ================================================================================
#define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type), alignof(Type), zero_mem) #define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type), alignof(Type), zero_mem)
#define Dqn_Arena_NewCopy(arena, Type, src) (Type *)Dqn_Arena_Copy(arena, src, sizeof(*src), alignof(Type))
#define Dqn_Arena_NewCopyZ(arena, Type, src) (Type *)Dqn_Arena_Copy(arena, src, sizeof(*src), alignof(Type))
#define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type) * count, alignof(Type), zero_mem) #define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type) * count, alignof(Type), zero_mem)
#define Dqn_Arena_NewCopy(arena, Type, src, count) (Type *)Dqn_Arena_Copy(arena, src, sizeof(*src) * count, alignof(Type)) #define Dqn_Arena_NewArrayCopy(arena, Type, src, count) (Type *)Dqn_Arena_Copy(arena, src, sizeof(*src) * count, alignof(Type))
#define Dqn_Arena_NewCopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ(arena, src, sizeof(*src) * count, alignof(Type)) #define Dqn_Arena_NewArrayCopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ(arena, src, sizeof(*src) * count, alignof(Type))
DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena); DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena);
DQN_API Dqn_MemBlock * Dqn_Arena_Grow (Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags); DQN_API Dqn_MemBlock * Dqn_Arena_Grow (Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags);

View File

@ -94,7 +94,7 @@ DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int
return (char *)buf; 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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -102,7 +102,7 @@ DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, char const *fmt, ...)
va_end(args); 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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -110,13 +110,13 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style,
va_end(args); 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]; char buffer[STB_SPRINTF_MIN];
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args); 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 (fmt) {
if (style.colour) 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_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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -143,7 +143,7 @@ DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...)
va_end(args); 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_StdFV(std_handle, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n")); 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_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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -163,7 +163,7 @@ DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style
va_end(args); 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_StdFVStyle(std_handle, style, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n")); 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) #if !defined(DQN_NO_FS)
// NOTE: [$FSYS] Dqn_Fs ============================================================================ // NOTE: [$FSYS] Dqn_Fs ============================================================================
#if defined(DQN_OS_WIN32) #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 = {}; ULARGE_INTEGER time_large_int = {};
time_large_int.u.LowPart = time->dwLowDateTime; 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) DQN_API bool Dqn_Fs_Exists(Dqn_String8 path)
{ {
bool result = false; bool result = false;
if (!Dqn_String8_IsValid(path))
return result;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
wchar_t path16[DQN_OS_WIN32_MAX_PATH]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path);
if (path16_size) { if (path16.size) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; 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) && result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
!(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); !(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) DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path)
{ {
bool result = false; bool result = false;
if (!Dqn_String8_IsValid(path))
return result;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
wchar_t path16[DQN_OS_WIN32_MAX_PATH]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path);
if (path16_size) { if (path16.size) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; 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) && result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); (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_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path)
{ {
Dqn_FsInfo result = {}; Dqn_FsInfo result = {};
if (!Dqn_String8_IsValid(path))
return result;
#if defined(DQN_OS_WIN32) #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 = {}; WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
wchar_t path16[DQN_OS_WIN32_MAX_PATH]; if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16));
if (!GetFileAttributesExW(path16, GetFileExInfoStandard, &attrib_data))
return result; return result;
result.exists = true; result.exists = true;
result.create_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftCreationTime); result.create_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftCreationTime);
result.last_access_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftLastAccessTime); result.last_access_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftLastAccessTime);
result.last_write_time_in_s = Dqn__WinFileTimeToSeconds(&attrib_data.ftLastWriteTime); result.last_write_time_in_s = Dqn_Win__FileTimeToSeconds(&attrib_data.ftLastWriteTime);
LARGE_INTEGER large_int = {}; LARGE_INTEGER large_int = {};
large_int.u.HighPart = DQN_CAST(int32_t)attrib_data.nFileSizeHigh; 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; bool result = false;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String16 src16 = Dqn_Win_String8ToString16Allocator(src, Dqn_Arena_Allocator(scratch.arena)); Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src);
Dqn_String16 dest16 = Dqn_Win_String8ToString16Allocator(dest, Dqn_Arena_Allocator(scratch.arena)); Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest);
int fail_if_exists = overwrite == false; int fail_if_exists = overwrite == false;
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
if (!result) { if (!result) {
Dqn_WinErrorMsg error = Dqn_Win_LastError(); Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
Dqn_Log_ErrorF("Copy file failed [src=\"%.*s\", dest=\"%.*s\", reason=\"%.*s\"]", Dqn_Log_ErrorF("Failed to copy the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s",
DQN_STRING_FMT(src), DQN_STRING_FMT(src),
DQN_STRING_FMT(dest), DQN_STRING_FMT(dest),
DQN_STRING_FMT(error)); DQN_STRING_FMT(error.msg));
} }
#elif defined(DQN_OS_UNIX) #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); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
bool result = true; bool result = true;
Dqn_usize path_indexes_size = 0;
uint16_t path_indexes[64] = {};
#if defined(DQN_OS_WIN32) #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 // NOTE: Go back from the end of the string to all the directories in the
// string, and try to create them. Since Win32 API cannot create // string, and try to create them. Since Win32 API cannot create
@ -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 // 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 // series of directories can not be made if a file exists with the same
// name. // 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); bool first_char = index == (path16.size - 1);
wchar_t ch = path16.data[index]; wchar_t ch = path16.data[index];
if (ch == '/' || ch == '\\' || first_char) { if (ch == '/' || ch == '\\' || first_char) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
wchar_t temp = path16.data[index]; 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 bool successful = GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data); // Check
if (!first_char) path16.data[index] = temp; // Undo null termination
if (successful) { if (successful) {
if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// NOTE: We found a directory, we can stop here and start // NOTE: The directory exists, continue iterating the path
// building up all the directories that didn't exist up to
// this point.
break;
} else { } else {
// NOTE: There's something that exists in at this path, but // NOTE: There's some kind of file that exists at the path
// it's not a directory. This request to make a directory is // but it's not a directory. This request to make a
// invalid. // directory is invalid.
return false; return false;
} }
} else { } else {
// NOTE: There's nothing that exists at this path, we can create // NOTE: There's nothing that exists at this path, we can create
// a directory here // a directory here
path_indexes[path_indexes_size++] = DQN_CAST(uint16_t)index;
}
}
}
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); result |= (CreateDirectoryW(path16.data, nullptr) == 0);
if (index != 0) path16.data[path_index] = temp; }
if (!first_char)
path16.data[index] = temp; // Undo null termination
}
} }
#elif defined(DQN_OS_UNIX) #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); Dqn_String8 copy = Dqn_String8_Copy(scratch.arena, path);
for (Dqn_usize index = copy.size - 1; index < copy.size; index--) { for (Dqn_usize index = copy.size - 1; index < copy.size; index--) {
bool first_char = index == (copy.size - 1); bool first_char = index == (copy.size - 1);
char ch = copy.data[index]; char ch = copy.data[index];
if (ch == '/' || first_char) { if (ch == '/' || first_char) {
char temp = copy.data[index]; 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); 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) { if (is_file) {
// NOTE: There's something that exists in at this path, but // 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) #if defined(DQN_OS_WIN32)
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String16 src16 = Dqn_Win_String8ToString16Allocator(src, Dqn_Arena_Allocator(scratch.arena)); Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src);
Dqn_String16 dest16 = Dqn_Win_String8ToString16Allocator(dest, Dqn_Arena_Allocator(scratch.arena)); Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest);
unsigned long flags = MOVEFILE_COPY_ALLOWED; unsigned long flags = MOVEFILE_COPY_ALLOWED;
if (overwrite) { 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; result = MoveFileExW(src16.data, dest16.data, flags) != 0;
if (!result) { 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(src),
DQN_STRING_FMT(dest), DQN_STRING_FMT(dest),
DQN_STRING_FMT(Dqn_Win_LastError())); DQN_STRING_FMT(error.msg));
} }
#elif defined(DQN_OS_UNIX) #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) DQN_API bool Dqn_Fs_Delete(Dqn_String8 path)
{ {
bool result = false; bool result = false;
if (!Dqn_String8_IsValid(path))
return result;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
wchar_t path16[DQN_OS_WIN32_MAX_PATH]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
int path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_ICOUNT(path16)); Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path);
if (path16_size) { if (path16.size) {
result = DeleteFileW(path16); result = DeleteFileW(path16.data);
if (!result) if (!result)
result = RemoveDirectoryW(path16); result = RemoveDirectoryW(path16.data);
} }
#elif defined(DQN_OS_UNIX) #elif defined(DQN_OS_UNIX)
result = remove(path.data) == 0; 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; (void)file_size;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
// NOTE: Convert to UTF16 // NOTE: Convert to UTF16 ==================================================
// -------------------------------------------------------------------------
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context);
Dqn_String8 path8 = Dqn_String8_Init(path, path_size); 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);
Dqn_WinErrorMsg error_msg = {};
// NOTE: Get the file handle // NOTE: Get the file handle ===============================================
// -------------------------------------------------------------------------
void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
/*DWORD dwDesiredAccess*/ GENERIC_READ, /*DWORD dwDesiredAccess*/ GENERIC_READ,
/*DWORD dwShareMode*/ 0, /*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, /*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_READONLY,
/*HANDLE hTemplateFile*/ nullptr); /*HANDLE hTemplateFile*/ nullptr);
if (file_handle == INVALID_HANDLE_VALUE) { if (file_handle == INVALID_HANDLE_VALUE) {
Dqn_Win_LastErrorToBuffer(&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)); Dqn_Log_ErrorF("Failed to open file for reading [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg));
return nullptr; return nullptr;
} }
DQN_DEFER { CloseHandle(file_handle); }; 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; LARGE_INTEGER win_file_size;
if (!GetFileSizeEx(file_handle, &win_file_size)) { if (!GetFileSizeEx(file_handle, &win_file_size)) {
Dqn_Win_LastErrorToBuffer(&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)); Dqn_Log_ErrorF("Failed to query file size [file=%.*s, reason=%.*s]", DQN_STRING_FMT(path8), DQN_STRING_FMT(error.msg));
return nullptr; 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) { if (read_result == 0) {
Dqn_Allocator_Dealloc(allocator, result, bytes_desired); Dqn_Allocator_Dealloc(allocator, result, bytes_desired);
Dqn_Win_LastErrorToBuffer(&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)); 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; return nullptr;
} }
if (bytes_read != bytes_desired) { 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_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_Log_ErrorF("'ReadFile' failed to read all bytes into file [file=%.*s, bytes_desired=%u, bytes_read=%u, reason=%.*s]",
DQN_STRING_FMT(path8), DQN_STRING_FMT(path8),
bytes_desired, bytes_desired,
bytes_read, bytes_read,
DQN_STRING_FMT(error_msg)); DQN_STRING_FMT(error.msg));
return nullptr; 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_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 path8 = Dqn_String8_Init(path, path_size); 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, void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
/*DWORD dwDesiredAccess*/ GENERIC_WRITE, /*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); /*HANDLE hTemplateFile*/ nullptr);
if (file_handle == INVALID_HANDLE_VALUE) { 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; return result;
} }
DQN_DEFER { CloseHandle(file_handle); }; 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_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, void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
/*DWORD dwDesiredAccess*/ access_mode, /*DWORD dwDesiredAccess*/ access_mode,
/*DWORD dwShareMode*/ 0, /*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); /*HANDLE hTemplateFile*/ nullptr);
if (handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE) {
Dqn_WinErrorMsg msg = Dqn_Win_LastError(); Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
result.error_size = result.error_size =
DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(result.error, DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(result.error,
DQN_ARRAY_UCOUNT(result.error), DQN_ARRAY_UCOUNT(result.error),
"Open file failed: %.*s for \"%.*s\"", "Open file failed: %.*s for \"%.*s\"",
DQN_STRING_FMT(msg), DQN_STRING_FMT(error.msg),
DQN_STRING_FMT(path)); DQN_STRING_FMT(path));
return result; return result;
} }
@ -842,12 +855,13 @@ DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize si
} }
if (!result) { 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 = file->error_size =
DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(file->error, DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(file->error,
DQN_ARRAY_UCOUNT(file->error), DQN_ARRAY_UCOUNT(file->error),
"Write file failed: %.*s for %.*s", "Write file failed: %.*s for %.*s",
DQN_STRING_FMT(msg)); DQN_STRING_FMT(error.msg));
} }
#else #else
result = fwrite(buffer, DQN_CAST(Dqn_usize)size, 1 /*count*/, file->handle) == 1 /*count*/; 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; 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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -957,7 +971,7 @@ DQN_API Dqn_String8 Dqn_FsPath_Convert(Dqn_Arena *arena, Dqn_String8 path)
return result; 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); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
va_list args; va_list args;
@ -1095,41 +1109,45 @@ DQN_API uint64_t Dqn_Date_EpochTime()
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
#if !defined(DQN_NO_WIN) #if !defined(DQN_NO_WIN)
// NOTE: [$WIND] Dqn_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(); Dqn_WinError result = {};
msg->data[0] = 0; result.code = GetLastError();
if (arena) {
unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
void *module_to_get_errors_from = nullptr; void *module_to_get_errors_from = nullptr;
if (msg->code >= 12000 && msg->code <= 12175) { // WinINET Errors if (result.code >= 12000 && result.code <= 12175) { // WinINET Errors
flags |= FORMAT_MESSAGE_FROM_HMODULE; flags |= FORMAT_MESSAGE_FROM_HMODULE;
module_to_get_errors_from = GetModuleHandleA("wininet.dll"); module_to_get_errors_from = GetModuleHandleA("wininet.dll");
} }
msg->size = FormatMessageA(flags, int32_t size = FormatMessageA(flags,
module_to_get_errors_from, // LPCVOID lpSource, module_to_get_errors_from, // LPCVOID lpSource,
msg->code, // unsigned long dwMessageId, result.code, // unsigned long dwMessageId,
0, // unsigned long dwLanguageId, 0, // unsigned long dwLanguageId,
msg->data, // LPSTR lpBuffer, nullptr, // LPSTR lpBuffer,
DQN_ARRAY_ICOUNT(msg->data), // unsigned long nSize, 0, // unsigned long nSize,
nullptr); // va_list * Arguments nullptr); // va_list * Arguments
if (msg->size >= 2 && if (size) {
(msg->data[msg->size - 2] == '\r' && msg->data[msg->size - 1] == '\n')) { Dqn_String8 buffer = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), size, Dqn_ZeroMem_No);
msg->size -= 2; 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;
}
} }
} }
DQN_API Dqn_WinErrorMsg Dqn_Win_LastError()
{
Dqn_WinErrorMsg result = {};
Dqn_Win_LastErrorToBuffer(&result);
return result; return result;
} }
DQN_API void Dqn_Win_MakeProcessDPIAware() DQN_API void Dqn_Win_MakeProcessDPIAware()
{ {
typedef bool SetProcessDpiAwareProc(void); 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 // 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. // version of windows, so we can call an older version of the API.
void *lib_handle = LoadLibraryA("user32.dll"); 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")) { 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); 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")) { } 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, ...) // NOTE: Windows UTF8 to String16 ==============================================
{ DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src)
// 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)
{ {
Dqn_String16 result = {}; Dqn_String16 result = {};
int required = Dqn_Win_String8ToCString16(src, nullptr, 0); if (!arena || !Dqn_String8_IsValid(src))
if (required > 0) { return result;
result.data = Dqn_Allocator_NewArray(allocator, wchar_t, required + 1, Dqn_ZeroMem_No);
if (result.data) { int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, nullptr /*dest*/, 0 /*dest size*/);
result.size = Dqn_Win_String8ToCString16(src, result.data, required + 1); if (required_size <= 0)
DQN_ASSERT(result.size == DQN_CAST(Dqn_usize)required); 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; return result;
} }
// NOTE: Windows String16 To UTF8 DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String8 src, wchar_t *dest, Dqn_usize dest_size)
// -----------------------------------------------------------------------------
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); int result = 0;
if (result && dest && dest_size > 0) 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; dest[DQN_MIN(result, dest_size - 1)] = 0;
return result; return result;
} }
DQN_API Dqn_String8 Dqn_Win_CString16ToString8Allocator(const wchar_t* src, int src_size, Dqn_Allocator allocator) // NOTE: Windows String16 To UTF8 ==================================================================
{ DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, Dqn_usize dest_size)
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)
{ {
int result = 0; int result = 0;
if (!Dqn_String8_IsValid(src))
return result;
int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size);
if (src_size) { if (src_size <= 0)
result = Dqn_Win_CString16ToCString8(src.data, src_size, dest, dest_size); 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; return result;
} }
DQN_API Dqn_String8 Dqn_Win_String16ToString8Allocator(Dqn_String16 src, Dqn_Allocator allocator) DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src)
{ {
Dqn_String8 result = {}; Dqn_String8 result = {};
if (!arena || !Dqn_String8_IsValid(src))
return result;
int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size);
if (src_size) { if (src_size <= 0)
result = Dqn_Win_CString16ToString8Allocator(src.data, src_size, allocator); 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*/);
} }
return result; return result;
} }
// NOTE: Windows Executable Directory // NOTE: Windows Executable Directory ==========================================
// ----------------------------------------------------------------------------- DQN_API Dqn_String16 Dqn_Win_EXEDirW(Dqn_Arena *arena)
DQN_API Dqn_usize Dqn_Win_EXEDirW(wchar_t *buffer, Dqn_usize size)
{ {
wchar_t module_path[DQN_OS_WIN32_MAX_PATH]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
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) {
return result;
}
DQN_MEMCPY(buffer, module_path, sizeof(wchar_t) * result);
return result;
}
DQN_API Dqn_String16 Dqn_Win_EXEDirWArena(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_String16 result = {}; Dqn_String16 result = {};
if (dir_size > 0) { Dqn_usize module_size = 0;
result.data = Dqn_Arena_NewCopyZ(arena, wchar_t, dir, dir_size); wchar_t *module_path = nullptr;
if (result.data) { do {
result.size = dir_size; 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_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; return result;
} }
DQN_API Dqn_String8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_String8 suffix) DQN_API Dqn_String8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_String8 suffix)
{ {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); 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_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; 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) if (!Dqn_String8_IsValid(path) || !it || path.size <= 0)
return false; return false;
wchar_t path16[DQN_OS_WIN32_MAX_PATH + 1]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
path16[0] = 0;
Dqn_usize path16_size = 0;
Dqn_Win_FolderIteratorW wide_it = {}; Dqn_Win_FolderIteratorW wide_it = {};
Dqn_String16 path16 = {};
if (it->handle) { if (it->handle) {
wide_it.handle = it->handle; wide_it.handle = it->handle;
} else { } 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("\\*")) || bool has_glob = Dqn_String8_EndsWith(path, DQN_STRING8("\\*")) ||
Dqn_String8_EndsWith(path, DQN_STRING8("/*")); Dqn_String8_EndsWith(path, DQN_STRING8("/*"));
path16_size = Dqn_Win_String8ToCString16(path, path16, DQN_ARRAY_UCOUNT(path16)); Dqn_String8 adjusted_path = path;
if (path16_size <= 0) // Conversion error
return false;
if (!has_glob) { if (!has_glob) {
// NOTE: We are missing the glob for enumerating the files, we will // NOTE: We are missing the glob for enumerating the files, we will
// add those characters in this branch, so overwrite the null // add those characters in this branch, so overwrite the null
// character, add the glob and re-null terminate the buffer. // character, add the glob and re-null terminate the buffer.
Dqn_usize space = DQN_OS_WIN32_MAX_PATH - path16_size; if (needs_asterisks)
if (needs_asterisks) { adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s*", DQN_STRING_FMT(path));
if (space < 2) else
return false; adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/*", DQN_STRING_FMT(path));
path16[path16_size++] = L'*';
} else {
if (space < 3)
return false;
path16[path16_size++] = L'\\';
path16[path16_size++] = L'*';
}
path16[path16_size++] = 0;
}
DQN_ASSERT(path16_size <= DQN_OS_WIN32_MAX_PATH);
} }
bool result = Dqn_Win_FolderWIterate(Dqn_String16{path16, path16_size}, &wide_it); path16 = Dqn_Win_String8ToString16(scratch.arena, adjusted_path);
if (path16.size <= 0) // Conversion error
return false;
}
bool result = Dqn_Win_FolderWIterate(path16, &wide_it);
it->handle = wide_it.handle; it->handle = wide_it.handle;
if (result) { 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); 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 // Seperate the URL into bits and bobs
Dqn_WinNetHandle result = {}; Dqn_WinNetHandle result = {};
if (!InternetCrackUrlA(url, url_size, 0 /*flags*/, &components)) { 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; 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)) { if (!HttpSendRequestA(handle->http_handle, nullptr /*headers*/, 0 /*headers length*/, (char *)post_data, post_data_size)) {
handle->state = Dqn_WinNetHandleState_RequestFailed; 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]", Dqn_Log_ErrorF("Failed to send request to %.*s [reason=%.*s]",
handle->host_name_size, handle->host_name_size,
handle->host_name, handle->host_name,
DQN_STRING_FMT(Dqn_Win_LastError())); DQN_STRING_FMT(error.msg));
return result; 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) #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 = {}; Dqn_String8 result = {};
if (!arena)
return result;
#if defined(DQN_OS_WIN32) #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)); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
result = Dqn_Win_CString16ToString8Allocator(exe_dir, DQN_CAST(int)exe_dir_size, allocator); Dqn_String16 exe_dir16 = Dqn_Win_EXEDirW(scratch.arena);
result = Dqn_Win_String16ToString8(arena, exe_dir16);
#elif defined(DQN_OS_UNIX) #elif defined(DQN_OS_UNIX)
int required_size_wo_null_terminator = 0; int required_size_wo_null_terminator = 0;
for (int try_size = 128; for (int try_size = 128;; try_size *= 2) {
;
try_size *= 2)
{
auto scoped_arena = Dqn_ArenaTempMemoryScope(arena); auto scoped_arena = Dqn_ArenaTempMemoryScope(arena);
char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No); char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No);
int bytes_written = readlink("/proc/self/exe", try_buf, try_size); 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 // Failed, we're unable to determine the executable directory
break; break;
} } else if (bytes_written == try_size) {
else if (bytes_written == try_size)
{
// Try again, if returned size was equal- we may of prematurely // Try again, if returned size was equal- we may of prematurely
// truncated according to the man pages // truncated according to the man pages
continue; continue;
} } else {
else
{
// readlink will give us the path to the executable. Once we // readlink will give us the path to the executable. Once we
// determine the correct buffer size required to get the full file // determine the correct buffer size required to get the full file
// path, we do some post-processing on said string and extract just // 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"); 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; required_size_wo_null_terminator = bytes_written;
for (int index_of_last_slash = bytes_written; for (Dqn_isize index_of_last_slash = bytes_written; index_of_last_slash >= 0; index_of_last_slash--) {
index_of_last_slash >= 0; if (try_buf[index_of_last_slash] == '/') {
index_of_last_slash--)
{
if (try_buf[index_of_last_slash] == '/')
{
// NOTE: We take the index of the last slash and not // NOTE: We take the index of the last slash and not
// (index_of_last_slash + 1) because we want to exclude the // (index_of_last_slash + 1) because we want to exclude the
// trailing backslash as a convention of this library. // trailing backslash as a convention of this library.
@ -1919,27 +1917,22 @@ DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Allocator allocator)
break; break;
} }
} }
break; break;
} }
} }
if (required_size_wo_null_terminator) if (required_size_wo_null_terminator) {
{
Dqn_ArenaTempMemory scope = Dqn_Arena_BeginTempMemory(arena); Dqn_ArenaTempMemory scope = Dqn_Arena_BeginTempMemory(arena);
char *exe_path = Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No); 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; exe_path[required_size_wo_null_terminator] = 0;
int bytes_written = readlink("/proc/self/exe", exe_path, required_size_wo_null_terminator); 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 // Note that if read-link fails again can be because there's
// a potential race condition here, our exe or directory could have // a potential race condition here, our exe or directory could have
// been deleted since the last call, so we need to be careful. // been deleted since the last call, so we need to be careful.
Dqn_Arena_EndTempMemory(scope); Dqn_Arena_EndTempMemory(scope);
} } else {
else
{
result = Dqn_String8_Init(exe_path, required_size_wo_null_terminator); result = Dqn_String8_Init(exe_path, required_size_wo_null_terminator);
} }
} }

View File

@ -48,20 +48,20 @@ DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
// NOTE: Print ===================================================================================== // NOTE: Print =====================================================================================
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string); 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_StdF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
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);
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); 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_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, 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);
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string); 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_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
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_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); 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_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, 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);
// NOTE: ANSI Formatting Codes ===================================================================== // NOTE: ANSI Formatting Codes =====================================================================
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b); 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_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_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 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_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_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_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("\\")) #define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\"))
@ -285,14 +285,12 @@ DQN_API uint64_t Dqn_Date_EpochTime ();
// DPI aware on Windows and ensure that application UI is scaled up // DPI aware on Windows and ensure that application UI is scaled up
// appropriately for the monitor. // appropriately for the monitor.
struct Dqn_WinErrorMsg struct Dqn_WinError
{ {
unsigned long code; unsigned long code;
char data[DQN_KILOBYTES(64) - 1]; // Maximum error size Dqn_String8 msg;
unsigned long size;
}; };
DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg); DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena);
DQN_API Dqn_WinErrorMsg Dqn_Win_LastError();
DQN_API void Dqn_Win_MakeProcessDPIAware(); DQN_API void Dqn_Win_MakeProcessDPIAware();
// NOTE: Windows String8 <-> String16 =========================================== // NOTE: Windows String8 <-> String16 ===========================================
@ -308,16 +306,12 @@ DQN_API void Dqn_Win_MakeProcessDPIAware();
// written/required for conversion. 0 if there was a conversion error and can be // written/required for conversion. 0 if there was a conversion error and can be
// queried using 'Dqn_Win_LastError' // 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 Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src);
DQN_API int Dqn_Win_String8ToCString16 (Dqn_String8 src, wchar_t *dest, int dest_size); DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String16 src, char *dest, Dqn_usize dest_size);
DQN_API Dqn_String16 Dqn_Win_String8ToString16Allocator (Dqn_String8 src, Dqn_Allocator allocator); 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); // NOTE: Path navigation ===========================================================================
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: API ======================================================================================= // NOTE: API =======================================================================================
// @proc Dqn_Win_EXEDirW, Dqn_Win_EXEDirWArena // @proc Dqn_Win_EXEDirW, Dqn_Win_EXEDirWArena
// @desc Evaluate the current executable's directory that is running when this // @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]; char file_name_buf[512];
}; };
DQN_API Dqn_usize Dqn_Win_EXEDirW (wchar_t *buffer, Dqn_usize size); DQN_API Dqn_String16 Dqn_Win_EXEDirW (Dqn_Arena *arena);
DQN_API Dqn_String16 Dqn_Win_EXEDirWArena (Dqn_Arena *arena);
DQN_API Dqn_String8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_String8 suffix); 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 Dqn_String16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_String16 suffix);
DQN_API bool Dqn_Win_FolderIterate (Dqn_String8 path, Dqn_Win_FolderIterator *it); 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); DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) #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 #endif
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds); DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
DQN_API uint64_t Dqn_OS_PerfCounterNow (); DQN_API uint64_t Dqn_OS_PerfCounterNow ();

View File

@ -1,5 +1,5 @@
// NOTE: [$CSTR] Dqn_CString8 ====================================================================== // 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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -8,7 +8,7 @@ DQN_API Dqn_usize Dqn_CString8_FSize(char const *fmt, ...)
return result; 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_list args_copy;
va_copy(args_copy, args); va_copy(args_copy, args);
@ -47,12 +47,6 @@ DQN_API Dqn_String8 Dqn_String8_InitCString8(char const *src)
return result; 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) DQN_API bool Dqn_String8_IsAll(Dqn_String8 string, Dqn_String8IsAll is_all)
{ {
bool result = Dqn_String8_IsValid(string); bool result = Dqn_String8_IsValid(string);
@ -90,6 +84,12 @@ DQN_API Dqn_String8 Dqn_String8_Slice(Dqn_String8 string, Dqn_usize offset, Dqn_
return result; return result;
} }
DQN_API Dqn_String8 Dqn_String8_Advance(Dqn_String8 string, Dqn_usize amount)
{
Dqn_String8 result = Dqn_String8_Slice(string, amount, UINT64_MAX);
return result;
}
DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray(Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size) DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray(Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size)
{ {
Dqn_String8BinarySplitResult result = {}; Dqn_String8BinarySplitResult result = {};
@ -176,6 +176,7 @@ DQN_API Dqn_String8FindResult Dqn_String8_FindFirstStringArray(Dqn_String8 strin
if (Dqn_String8_Eq(string_slice, find_item)) { if (Dqn_String8_Eq(string_slice, find_item)) {
result.found = true; result.found = true;
result.index = index; result.index = index;
result.start_to_before_match = Dqn_String8_Init(string.data, index);
result.match = Dqn_String8_Init(string.data + index, find_item.size); result.match = Dqn_String8_Init(string.data + index, find_item.size);
result.match_to_end_of_buffer = Dqn_String8_Init(result.match.data, string.size - index); result.match_to_end_of_buffer = Dqn_String8_Init(result.match.data, string.size - index);
break; break;
@ -357,14 +358,17 @@ DQN_API Dqn_String8 Dqn_String8_FileNameFromPath(Dqn_String8 path)
if (!Dqn_String8_IsValid(result)) if (!Dqn_String8_IsValid(result))
return result; return result;
for (Dqn_usize i = result.size - 1; i < result.size; --i) { DQN_MSVC_WARNING_PUSH
if (result.data[i] == '\\' || result.data[i] == '/') { 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; char const *end = result.data + result.size;
result.data = result.data + (i + 1); result.data = result.data + (index + 1);
result.size = end - result.data; result.size = end - result.data;
break; break;
} }
} }
DQN_MSVC_WARNING_POP
return result; return result;
} }
@ -541,7 +545,7 @@ DQN_API bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs)
} }
#endif #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_list va;
va_start(va, fmt); va_start(va, fmt);
@ -550,7 +554,7 @@ DQN_API Dqn_String8 Dqn_String8_InitF(Dqn_Allocator allocator, char const *fmt,
return result; 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 = {}; Dqn_String8 result = {};
if (!fmt) if (!fmt)
@ -625,7 +629,7 @@ DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_Stri
return result; 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); Dqn_String8 string = Dqn_String8_InitFV(builder->allocator, fmt, args);
if (string.size == 0) if (string.size == 0)
@ -637,7 +641,7 @@ DQN_API bool Dqn_String8Builder_AppendFV(Dqn_String8Builder *builder, char const
return result; 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_list args;
va_start(args, fmt); va_start(args, fmt);
@ -711,17 +715,18 @@ DQN_API uint8_t Dqn_Char_HexToU8(char ch)
return result; 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) DQN_API char Dqn_Char_ToHex(char ch)
{ {
char result = DQN_CAST(char)-1; 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; return result;
} }
DQN_API char Dqn_Char_ToHexUnchecked(char ch) 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; return result;
} }

View File

@ -17,8 +17,8 @@
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; } DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; }
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; } DQN_API template <Dqn_usize N> 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_FSize (DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
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);
DQN_API Dqn_usize Dqn_CString8_Size (char const *a); DQN_API Dqn_usize Dqn_CString8_Size (char const *a);
DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a); DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a);
@ -248,10 +248,11 @@ struct Dqn_String8BinarySplitResult
struct Dqn_String8FindResult struct Dqn_String8FindResult
{ {
bool found; bool found; // True if string was found. If false, the subsequent fields below are not set.
Dqn_usize index; Dqn_usize index; // The index in the buffer where the found string starts
Dqn_String8 match; Dqn_String8 match; // The matching string in the buffer that was searched
Dqn_String8 match_to_end_of_buffer; Dqn_String8 match_to_end_of_buffer; // The substring containing the found string to the end of the buffer
Dqn_String8 start_to_before_match; // The substring from the start of the buffer up until the found string, not including it
}; };
// NOTE: Macros ==================================================================================== // NOTE: Macros ====================================================================================
@ -307,16 +308,17 @@ struct Dqn_String8ToI64Result
}; };
DQN_API Dqn_String8 Dqn_String8_InitCString8 (char const *src); 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 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_InitF (Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE 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_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_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_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size);
DQN_API Dqn_String8 Dqn_String8_Copy (Dqn_Allocator allocator, Dqn_String8 string); DQN_API Dqn_String8 Dqn_String8_Copy (Dqn_Allocator allocator, Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8_Slice (Dqn_String8 string, Dqn_usize offset, Dqn_usize size); DQN_API Dqn_String8 Dqn_String8_Slice (Dqn_String8 string, Dqn_usize offset, Dqn_usize size);
DQN_API Dqn_String8 Dqn_String8_Advance (Dqn_String8 string, Dqn_usize amount);
DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray (Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size); DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray (Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size);
DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplit (Dqn_String8 string, Dqn_String8 find); DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplit (Dqn_String8 string, Dqn_String8 find);
DQN_API Dqn_usize Dqn_String8_Split (Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count); DQN_API Dqn_usize Dqn_String8_Split (Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count);
@ -436,11 +438,11 @@ template <Dqn_usize N> struct Dqn_FString8
char const *end () const { return data + size; } char const *end () const { return data + size; }
}; };
template <Dqn_usize N> Dqn_FString8<N> Dqn_FString8_InitF (char const *fmt, ...); template <Dqn_usize N> Dqn_FString8<N> Dqn_FString8_InitF (DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
template <Dqn_usize N> Dqn_usize Dqn_FString8_Max (Dqn_FString8<N> const *string); template <Dqn_usize N> Dqn_usize Dqn_FString8_Max (Dqn_FString8<N> const *string);
template <Dqn_usize N> void Dqn_FString8_Clear (Dqn_FString8<N> *string); template <Dqn_usize N> void Dqn_FString8_Clear (Dqn_FString8<N> *string);
template <Dqn_usize N> bool Dqn_FString8_AppendFV (Dqn_FString8<N> *string, char const *fmt, va_list va); template <Dqn_usize N> bool Dqn_FString8_AppendFV (Dqn_FString8<N> *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list va);
template <Dqn_usize N> bool Dqn_FString8_AppendF (Dqn_FString8<N> *string, char const *fmt, ...); template <Dqn_usize N> bool Dqn_FString8_AppendF (Dqn_FString8<N> *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
template <Dqn_usize N> bool Dqn_FString8_AppendCString8 (Dqn_FString8<N> *string, char const *value, Dqn_usize size); template <Dqn_usize N> bool Dqn_FString8_AppendCString8 (Dqn_FString8<N> *string, char const *value, Dqn_usize size);
template <Dqn_usize N> bool Dqn_FString8_Append (Dqn_FString8<N> *string, Dqn_String8 value); template <Dqn_usize N> bool Dqn_FString8_Append (Dqn_FString8<N> *string, Dqn_String8 value);
template <Dqn_usize N> Dqn_String8 Dqn_FString8_ToString8 (Dqn_FString8<N> const *string); template <Dqn_usize N> Dqn_String8 Dqn_FString8_ToString8 (Dqn_FString8<N> const *string);
@ -487,8 +489,8 @@ struct Dqn_String8Builder
Dqn_usize count; ///< The number of links in the linked list of strings 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_AppendF (Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
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_API bool Dqn_String8Builder_AppendRef (Dqn_String8Builder *builder, Dqn_String8 string); 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 bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8Builder_Build (Dqn_String8Builder const *builder, Dqn_Allocator allocator); DQN_API Dqn_String8 Dqn_String8Builder_Build (Dqn_String8Builder const *builder, Dqn_Allocator allocator);
@ -510,7 +512,7 @@ DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint);
#if !defined(DQN_NO_FSTRING8) #if !defined(DQN_NO_FSTRING8)
// NOTE: [$FSTR] Dqn_FString8 ====================================================================== // NOTE: [$FSTR] Dqn_FString8 ======================================================================
template <Dqn_usize N> Dqn_FString8<N> Dqn_FString8_InitF(char const *fmt, ...) template <Dqn_usize N> Dqn_FString8<N> Dqn_FString8_InitF(DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{ {
Dqn_FString8<N> result = {}; Dqn_FString8<N> result = {};
if (fmt) { if (fmt) {
@ -533,7 +535,7 @@ template <Dqn_usize N> void Dqn_FString8_Clear(Dqn_FString8<N> *string)
*string = {}; *string = {};
} }
template <Dqn_usize N> bool Dqn_FString8_AppendFV(Dqn_FString8<N> *string, char const *fmt, va_list args) template <Dqn_usize N> bool Dqn_FString8_AppendFV(Dqn_FString8<N> *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{ {
bool result = false; bool result = false;
if (!string || !fmt) if (!string || !fmt)
@ -551,7 +553,7 @@ template <Dqn_usize N> bool Dqn_FString8_AppendFV(Dqn_FString8<N> *string, char
return result; return result;
} }
template <Dqn_usize N> bool Dqn_FString8_AppendF(Dqn_FString8<N> *string, char const *fmt, ...) template <Dqn_usize N> bool Dqn_FString8_AppendF(Dqn_FString8<N> *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{ {
bool result = false; bool result = false;
if (!string || !fmt) if (!string || !fmt)