From bf413d7e570c3b897256e5e94974738ebb661933 Mon Sep 17 00:00:00 2001 From: doyle Date: Wed, 25 Oct 2023 00:11:48 +1100 Subject: [PATCH] dqn: Upgrade lib to latest version from TELY --- Misc/dqn_unit_tests.cpp | 739 ++++++++++++++++++++++++---------------- build.bat | 2 +- dqn.h | 34 +- dqn_base.cpp | 208 ++++++----- dqn_base.h | 149 +++++--- dqn_containers.h | 686 +++++++++++++++++++++++++++---------- dqn_cppbuild.h | 183 ++++++++++ dqn_debug.cpp | 56 +-- dqn_debug.h | 18 +- dqn_external.h | 13 +- dqn_helpers.cpp | 281 +++++++++------ dqn_helpers.h | 232 ++++++++----- dqn_math.cpp | 332 +++++++++++++++++- dqn_math.h | 140 +++++--- dqn_memory.cpp | 133 +++++--- dqn_memory.h | 88 ++--- dqn_os.cpp | 136 ++++++++ dqn_os.h | 18 + dqn_platform.cpp | 580 +++++++++++++++---------------- dqn_platform.h | 152 +++++---- dqn_strings.cpp | 439 +++++++++++++----------- dqn_strings.h | 413 +++++++++++----------- dqn_win32.h | 38 +++ 23 files changed, 3274 insertions(+), 1796 deletions(-) create mode 100644 dqn_cppbuild.h create mode 100644 dqn_os.cpp create mode 100644 dqn_os.h diff --git a/Misc/dqn_unit_tests.cpp b/Misc/dqn_unit_tests.cpp index a8ca0e7..83f9b5c 100644 --- a/Misc/dqn_unit_tests.cpp +++ b/Misc/dqn_unit_tests.cpp @@ -162,79 +162,79 @@ static Dqn_UTest Dqn_Test_Bin() Dqn_UTest test = {}; DQN_UTEST_GROUP(test, "Dqn_Bin") { DQN_UTEST_TEST("Convert 0x123") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("0x123")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0x123")); DQN_UTEST_ASSERTF(&test, result == 0x123, "result: %zu", result); } DQN_UTEST_TEST("Convert 0xFFFF") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("0xFFFF")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xFFFF")); DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %zu", result); } DQN_UTEST_TEST("Convert FFFF") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("FFFF")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("FFFF")); DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %zu", result); } DQN_UTEST_TEST("Convert abCD") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("abCD")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("abCD")); DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %zu", result); } DQN_UTEST_TEST("Convert 0xabCD") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("0xabCD")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xabCD")); DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %zu", result); } DQN_UTEST_TEST("Convert 0x") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("0x")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0x")); DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %zu", result); } DQN_UTEST_TEST("Convert 0X") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("0X")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0X")); DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %zu", result); } DQN_UTEST_TEST("Convert 3") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("3")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("3")); DQN_UTEST_ASSERTF(&test, result == 3, "result: %zu", result); } DQN_UTEST_TEST("Convert f") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("f")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("f")); DQN_UTEST_ASSERTF(&test, result == 0xf, "result: %zu", result); } DQN_UTEST_TEST("Convert g") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("g")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("g")); DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result); } DQN_UTEST_TEST("Convert -0x3") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STRING8("-0x3")); + uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("-0x3")); DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result); } uint32_t number = 0xd095f6; DQN_UTEST_TEST("Convert %x to string", number) { - Dqn_String8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(number_hex, DQN_STRING8("f695d000")), "number_hex=%.*s", DQN_STRING_FMT(number_hex)); + Dqn_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex)); } number = 0xf6ed00; DQN_UTEST_TEST("Convert %x to string", number) { - Dqn_String8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(number_hex, DQN_STRING8("00edf600")), "number_hex=%.*s", DQN_STRING_FMT(number_hex)); + Dqn_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("00edf600")), "number_hex=%.*s", DQN_STR_FMT(number_hex)); } - Dqn_String8 hex = DQN_STRING8("0xf6ed00"); - DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STRING_FMT(hex)) { - Dqn_String8 bytes = Dqn_Bin_HexToBytesArena(scratch.arena, hex); + Dqn_Str8 hex = DQN_STR8("0xf6ed00"); + DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) { + Dqn_Str8 bytes = Dqn_Bin_HexToBytesArena(scratch.arena, hex); DQN_UTEST_ASSERTF(&test, - Dqn_String8_Eq(bytes, DQN_STRING8("\xf6\xed\x00")), + Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")), "number_hex=%.*s", - DQN_STRING_FMT(Dqn_Bin_BytesToHexArena(scratch.arena, bytes.data, bytes.size))); + DQN_STR_FMT(Dqn_Bin_BytesToHexArena(scratch.arena, bytes.data, bytes.size))); } } @@ -247,166 +247,303 @@ static Dqn_UTest Dqn_Test_BinarySearch() Dqn_UTest test = {}; DQN_UTEST_GROUP(test, "Dqn_BinarySearch") { DQN_UTEST_TEST("Search array of 1 item") { - uint32_t array[] = {1}; + uint32_t array[] = {1}; + Dqn_BinarySearchResult result = {}; - Dqn_BinarySearchResult result = {}; - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_Match); + // NOTE: Match ============================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_OnePastMatch); + // NOTE: Lower bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); + + // NOTE: Upper bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_UpperBound); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 1); } DQN_UTEST_TEST("Search array of 2 items") { - uint32_t array[] = {1, 2}; + uint32_t array[] = {1}; + Dqn_BinarySearchResult result = {}; - Dqn_BinarySearchResult result = {}; - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_Match); + // NOTE: Match ============================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_Match); - DQN_UTEST_ASSERT(&test, result.found); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_Match); + DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_Match); - DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_OnePastMatch); + // NOTE: Lower bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_LowerBound); DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_OnePastMatch); + // NOTE: Upper bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_UpperBound); DQN_UTEST_ASSERT(&test, result.found); - DQN_UTEST_ASSERT(&test, result.index == 2); + DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_UpperBound); DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 2); + DQN_UTEST_ASSERT(&test, result.index == 1); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); } DQN_UTEST_TEST("Search array of 3 items") { - uint32_t array[] = {1, 2, 3}; + uint32_t array[] = {1, 2, 3}; + Dqn_BinarySearchResult result = {}; - Dqn_BinarySearchResult result = {}; - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_Match); + // NOTE: Match ============================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 2); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, !result.found); - DQN_UTEST_ASSERT(&test, result.index == 0); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, result.found); - DQN_UTEST_ASSERT(&test, result.index == 1); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, result.found); - DQN_UTEST_ASSERT(&test, result.index == 2); - - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 3); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4 /*find*/, Dqn_BinarySearchType_OnePastMatch); + // NOTE: Lower bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 3); + + // NOTE: Upper bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 3); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_UpperBound); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 3); } DQN_UTEST_TEST("Search array of 4 items") { - uint32_t array[] = {1, 2, 3, 4}; + uint32_t array[] = {1, 2, 3, 4}; + Dqn_BinarySearchResult result = {}; - Dqn_BinarySearchResult result = {}; - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_Match); + // NOTE: Match ============================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 2); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 3); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 5 /*find*/, Dqn_BinarySearchType_Match); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 5U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 4); + + // NOTE: Lower bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0 /*find*/, Dqn_BinarySearchType_OnePastMatch); - DQN_UTEST_ASSERT(&test, !result.found); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 0); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_LowerBound); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 1); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_LowerBound); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 2); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_LowerBound); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 3); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 5U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 4); + + // NOTE: Upper bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 1); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 3); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 4); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 5U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 4); + } + + DQN_UTEST_TEST("Search array with duplicate items") { + uint32_t array[] = {1, 1, 2, 2, 3}; + Dqn_BinarySearchResult result = {}; + + // NOTE: Match ============================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_Match); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_Match); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_Match); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 4); - result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 5 /*find*/, Dqn_BinarySearchType_OnePastMatch); + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_Match); DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 5); + + // NOTE: Lower bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 4); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4U /*find*/, Dqn_BinarySearchType_LowerBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 5); + + // NOTE: Upper bound ======================================================================= + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 0U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 0); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 1U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 2); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 2U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, result.found); + DQN_UTEST_ASSERT(&test, result.index == 4); + + result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 3U /*find*/, Dqn_BinarySearchType_UpperBound); + DQN_UTEST_ASSERT(&test, !result.found); + DQN_UTEST_ASSERT(&test, result.index == 5); } } return test; @@ -423,23 +560,23 @@ static Dqn_UTest Dqn_Test_DSMap() DQN_DEFER { Dqn_DSMap_Deinit(&map); }; DQN_UTEST_TEST("Find non-existent value") { - uint64_t *value = Dqn_DSMap_Find(&map, Dqn_DSMap_KeyCStringLit(&map, "Foo")); + uint64_t *value = Dqn_DSMap_FindKeyStr8(&map, DQN_STR8("Foo")).value; DQN_UTEST_ASSERT(&test, !value); DQN_UTEST_ASSERT(&test, map.size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.initial_size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/); } - Dqn_DSMapKey key = Dqn_DSMap_KeyCStringLit(&map, "Bar"); + Dqn_DSMapKey key = Dqn_DSMap_KeyCStr8(&map, "Bar"); DQN_UTEST_TEST("Insert value and lookup") { uint64_t desired_value = 0xF00BAA; - uint64_t *slot_value = Dqn_DSMap_Set(&map, key, desired_value, nullptr /*found*/); + uint64_t *slot_value = Dqn_DSMap_Set(&map, key, desired_value).value; DQN_UTEST_ASSERT(&test, slot_value); DQN_UTEST_ASSERT(&test, map.size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.initial_size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.occupied == 2); - uint64_t *value = Dqn_DSMap_Find(&map, key); + uint64_t *value = Dqn_DSMap_Find(&map, key).value; DQN_UTEST_ASSERT(&test, value); DQN_UTEST_ASSERT(&test, *value == desired_value); } @@ -454,10 +591,10 @@ static Dqn_UTest Dqn_Test_DSMap() enum DSMapTestType { DSMapTestType_Set, DSMapTestType_MakeSlot, DSMapTestType_Count }; for (int test_type = 0; test_type < DSMapTestType_Count; test_type++) { - Dqn_String8 prefix = {}; + Dqn_Str8 prefix = {}; switch (test_type) { - case DSMapTestType_Set: prefix = DQN_STRING8("Set"); break; - case DSMapTestType_MakeSlot: prefix = DQN_STRING8("Make slot"); break; + case DSMapTestType_Set: prefix = DQN_STR8("Set"); break; + case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break; } Dqn_Arena_TempMemoryScope(scratch.arena); @@ -465,24 +602,22 @@ static Dqn_UTest Dqn_Test_DSMap() Dqn_DSMap map = Dqn_DSMap_Init(MAP_SIZE); DQN_DEFER { Dqn_DSMap_Deinit(&map); }; - DQN_UTEST_TEST("%.*s: Test growing", DQN_STRING_FMT(prefix)) { + DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) { uint64_t map_start_size = map.size; uint64_t value = 0; uint64_t grow_threshold = map_start_size * 3 / 4; for (; map.occupied != grow_threshold; value++) { uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); - DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key)); - DQN_UTEST_ASSERT(&test, !Dqn_DSMap_FindSlot(&map, key)); - bool found = false; + DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key).value); + Dqn_DSMapResult make_result = {}; if (test_type == DSMapTestType_Set) { - Dqn_DSMap_Set(&map, key, value, &found); + make_result = Dqn_DSMap_Set(&map, key, value); } else { - Dqn_DSMap_MakeSlot(&map, key, &found); + make_result = Dqn_DSMap_Make(&map, key); } - DQN_UTEST_ASSERT(&test, !found); - DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key)); - DQN_UTEST_ASSERT(&test, Dqn_DSMap_FindSlot(&map, key)); + DQN_UTEST_ASSERT(&test, !make_result.found); + DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key).value); } DQN_UTEST_ASSERT(&test, map.initial_size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.size == map_start_size); @@ -491,28 +626,28 @@ static Dqn_UTest Dqn_Test_DSMap() { // NOTE: One more item should cause the table to grow by 2x uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); - bool found = false; + Dqn_DSMapResult make_result = {}; if (test_type == DSMapTestType_Set) { - Dqn_DSMap_Set(&map, key, value, &found); + make_result = Dqn_DSMap_Set(&map, key, value); } else { - Dqn_DSMap_MakeSlot(&map, key, &found); + make_result = Dqn_DSMap_Make(&map, key); } value++; - DQN_UTEST_ASSERT(&test, !found); + DQN_UTEST_ASSERT(&test, !make_result.found); DQN_UTEST_ASSERT(&test, map.size == map_start_size * 2); DQN_UTEST_ASSERT(&test, map.initial_size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value); } } - DQN_UTEST_TEST("%.*s: Check the sentinel is present", DQN_STRING_FMT(prefix)) { + DQN_UTEST_TEST("%.*s: Check the sentinel is present", DQN_STR_FMT(prefix)) { Dqn_DSMapSlot NIL_SLOT = {}; Dqn_DSMapSlot sentinel = map.slots[DQN_DS_MAP_SENTINEL_SLOT]; DQN_UTEST_ASSERT(&test, DQN_MEMCMP(&sentinel, &NIL_SLOT, sizeof(NIL_SLOT)) == 0); } - DQN_UTEST_TEST("%.*s: Recheck all the hash tables values after growing", DQN_STRING_FMT(prefix)) { + DQN_UTEST_TEST("%.*s: Recheck all the hash tables values after growing", DQN_STR_FMT(prefix)) { for (uint64_t index = 1 /*Sentinel*/; index < map.occupied; index++) { Dqn_DSMapSlot const *slot = map.slots + index; @@ -528,12 +663,12 @@ static Dqn_UTest Dqn_Test_DSMap() DQN_UTEST_ASSERT(&test, slot->key.hash == Dqn_DSMap_Hash(&map, slot->key)); // NOTE: Check the reverse lookup is correct - Dqn_DSMapSlot const *check = Dqn_DSMap_FindSlot(&map, slot->key); - DQN_UTEST_ASSERT(&test, slot == check); + Dqn_DSMapResult check = Dqn_DSMap_Find(&map, slot->key); + DQN_UTEST_ASSERT(&test, slot->value == *check.value); } } - DQN_UTEST_TEST("%.*s: Test shrinking", DQN_STRING_FMT(prefix)) { + DQN_UTEST_TEST("%.*s: Test shrinking", DQN_STR_FMT(prefix)) { uint64_t start_map_size = map.size; uint64_t start_map_occupied = map.occupied; uint64_t value = 0; @@ -542,11 +677,9 @@ static Dqn_UTest Dqn_Test_DSMap() 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_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key)); - DQN_UTEST_ASSERT(&test, Dqn_DSMap_FindSlot(&map, key)); + DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key).value); Dqn_DSMap_Erase(&map, key); - DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key)); - DQN_UTEST_ASSERT(&test, !Dqn_DSMap_FindSlot(&map, key)); + DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key).value); } DQN_UTEST_ASSERT(&test, map.size == start_map_size); DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value); @@ -575,27 +708,27 @@ static Dqn_UTest Dqn_Test_DSMap() Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)&value_test, sizeof(value_test)); // NOTE: Validate each slot value - Dqn_DSMapSlot const *slot = Dqn_DSMap_FindSlot(&map, key); - DQN_UTEST_ASSERT(&test, slot); - DQN_UTEST_ASSERT(&test, slot->key == key); + Dqn_DSMapResult find_result = Dqn_DSMap_Find(&map, key); + DQN_UTEST_ASSERT(&test, find_result.value); + DQN_UTEST_ASSERT(&test, find_result.slot->key == key); if (test_type == DSMapTestType_Set) { - DQN_UTEST_ASSERT(&test, slot->value == value_test); + DQN_UTEST_ASSERT(&test, *find_result.value == value_test); } else { - DQN_UTEST_ASSERT(&test, slot->value == 0); // NOTE: Make slot does not set the key so should be 0 + DQN_UTEST_ASSERT(&test, *find_result.value == 0); // NOTE: Make slot does not set the key so should be 0 } - DQN_UTEST_ASSERT(&test, slot->key.hash == Dqn_DSMap_Hash(&map, slot->key)); + DQN_UTEST_ASSERT(&test, find_result.slot->key.hash == Dqn_DSMap_Hash(&map, find_result.slot->key)); // NOTE: Check the reverse lookup is correct - Dqn_DSMapSlot const *check = Dqn_DSMap_FindSlot(&map, slot->key); - DQN_UTEST_ASSERT(&test, slot == check); + Dqn_DSMapResult check = Dqn_DSMap_Find(&map, find_result.slot->key); + DQN_UTEST_ASSERT(&test, *find_result.value == *check.value); } for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value); Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy)); - DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key)); + DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find(&map, key).value); Dqn_DSMap_Erase(&map, key); - DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key)); + DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find(&map, key).value); } DQN_UTEST_ASSERT(&test, map.initial_size == MAP_SIZE); DQN_UTEST_ASSERT(&test, map.size == map.initial_size); @@ -606,18 +739,18 @@ static Dqn_UTest Dqn_Test_DSMap() return test; } -static Dqn_UTest Dqn_Test_FString8() +static Dqn_UTest Dqn_Test_FStr8() { Dqn_UTest test = {}; - DQN_UTEST_GROUP(test, "Dqn_FString8") { + DQN_UTEST_GROUP(test, "Dqn_FStr8") { DQN_UTEST_TEST("Append too much fails") { - Dqn_FString8<4> str = {}; - DQN_UTEST_ASSERT(&test, !Dqn_FString8_Append(&str, DQN_STRING8("abcde"))); + Dqn_FStr8<4> str = {}; + DQN_UTEST_ASSERT(&test, !Dqn_FStr8_Append(&str, DQN_STR8("abcde"))); } DQN_UTEST_TEST("Append format string too much fails") { - Dqn_FString8<4> str = {}; - DQN_UTEST_ASSERT(&test, !Dqn_FString8_AppendF(&str, "abcde")); + Dqn_FStr8<4> str = {}; + DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AppendF(&str, "abcde")); } } return test; @@ -628,37 +761,37 @@ static Dqn_UTest Dqn_Test_Fs() Dqn_UTest test = {}; DQN_UTEST_GROUP(test, "Dqn_Fs") { DQN_UTEST_TEST("Make directory recursive \"abcd/efgh\"") { - DQN_UTEST_ASSERTF(&test, Dqn_Fs_MakeDir(DQN_STRING8("abcd/efgh")), "Failed to make directory"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STRING8("abcd")), "Directory was not made"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STRING8("abcd/efgh")), "Subdirectory was not made"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STRING8("abcd")) == false, "This function should only return true for files"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STRING8("abcd/efgh")) == false, "This function should only return true for files"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STRING8("abcd/efgh")), "Failed to delete directory"); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STRING8("abcd")), "Failed to cleanup directory"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_MakeDir(DQN_STR8("abcd/efgh")), "Failed to make directory"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd")), "Directory was not made"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd/efgh")), "Subdirectory was not made"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd")) == false, "This function should only return true for files"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd/efgh")) == false, "This function should only return true for files"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd/efgh")), "Failed to delete directory"); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd")), "Failed to cleanup directory"); } DQN_UTEST_TEST("Write file, read it, copy it, move it and delete it") { // NOTE: Write step - Dqn_String8 const SRC_FILE = DQN_STRING8("dqn_test_file"); - Dqn_b32 write_result = Dqn_Fs_WriteCString8(SRC_FILE.data, SRC_FILE.size, "test", 4); + Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file"); + Dqn_b32 write_result = Dqn_Fs_WriteCStr8(SRC_FILE.data, SRC_FILE.size, "test", 4); DQN_UTEST_ASSERT(&test, write_result); DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(SRC_FILE)); // NOTE: Read step Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator); - DQN_UTEST_ASSERTF(&test, Dqn_String8_IsValid(read_file), "Failed to load file"); + Dqn_Str8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_IsValid(read_file), "Failed to load file"); DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes"); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(read_file, DQN_STRING8("test")), "read(%zu): %.*s", read_file.size, DQN_STRING_FMT(read_file)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file)); // NOTE: Copy step - Dqn_String8 const COPY_FILE = DQN_STRING8("dqn_test_file_copy"); + Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy"); Dqn_b32 copy_result = Dqn_Fs_Copy(SRC_FILE, COPY_FILE, true /*overwrite*/); DQN_UTEST_ASSERT(&test, copy_result); DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(COPY_FILE)); // NOTE: Move step - Dqn_String8 const MOVE_FILE = DQN_STRING8("dqn_test_file_move"); + Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move"); Dqn_b32 move_result = Dqn_Fs_Move(COPY_FILE, MOVE_FILE, true /*overwrite*/); DQN_UTEST_ASSERT(&test, move_result); DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(MOVE_FILE)); @@ -714,7 +847,7 @@ static Dqn_UTest Dqn_Test_FixedArray() int const ITEM = 2; int raw_array[] = {1}; auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_UCOUNT(raw_array)); - Dqn_FArray_Add(&array, &ITEM, 1); + Dqn_FArray_Add(&array, ITEM); DQN_UTEST_ASSERT(&test, array.size == 2); DQN_UTEST_ASSERT(&test, array.data[0] == 1); DQN_UTEST_ASSERT(&test, array.data[1] == ITEM); @@ -824,17 +957,17 @@ enum Dqn_Tests__HashType #undef DQN_UTEST_HASH_X_ENTRY }; -Dqn_String8 const DQN_UTEST_HASH_STRING_[] = +Dqn_Str8 const DQN_UTEST_HASH_STRING_[] = { -#define DQN_UTEST_HASH_X_ENTRY(enum_val, string) DQN_STRING8(string), +#define DQN_UTEST_HASH_X_ENTRY(enum_val, string) DQN_STR8(string), DQN_UTEST_HASH_X_MACRO #undef DQN_UTEST_HASH_X_ENTRY }; -void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) +void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 input_hex = Dqn_Hex_BytesToString8Arena(scratch.arena, input.data, input.size); + Dqn_Str8 input_hex = Dqn_Hex_BytesToStr8Arena(scratch.arena, input.data, input.size); switch(hash_type) { @@ -849,7 +982,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).data), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).data)); } @@ -866,7 +999,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).data), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).data)); } @@ -883,7 +1016,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).data), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).data)); } @@ -900,7 +1033,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).data), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).data)); } @@ -917,7 +1050,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).data), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).data)); } @@ -934,7 +1067,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).data), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).data)); } @@ -951,7 +1084,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).data), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).data)); } @@ -968,7 +1101,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) "\nhash: %.*s" "\nexpect: %.*s" , - DQN_STRING_FMT(input_hex), + DQN_STR_FMT(input_hex), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).data), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).data)); } @@ -980,12 +1113,12 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_String8 input) Dqn_UTest Dqn_Test_Keccak() { Dqn_UTest test = {}; - Dqn_String8 const INPUTS[] = { - DQN_STRING8("abc"), - DQN_STRING8(""), - DQN_STRING8("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), - DQN_STRING8("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmno" - "pqrstnopqrstu"), + Dqn_Str8 const INPUTS[] = { + DQN_STR8("abc"), + DQN_STR8(""), + DQN_STR8("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), + DQN_STR8("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmno" + "pqrstnopqrstu"), }; DQN_UTEST_GROUP(test, "Dqn_Keccak") @@ -994,13 +1127,13 @@ Dqn_UTest Dqn_Test_Keccak() pcg32_random_t rng = {}; pcg32_srandom_r(&rng, 0xd48e'be21'2af8'733d, 0x3f89'3bd2'd6b0'4eef); - for (Dqn_String8 input : INPUTS) { - Dqn_UTest_Begin(&test, "%.*s - Input: %.*s", DQN_STRING_FMT(DQN_UTEST_HASH_STRING_[hash_type]), DQN_CAST(int)DQN_MIN(input.size, 54), input.data); + for (Dqn_Str8 input : INPUTS) { + Dqn_UTest_Begin(&test, "%.*s - Input: %.*s", DQN_STR_FMT(DQN_UTEST_HASH_STRING_[hash_type]), DQN_CAST(int)DQN_MIN(input.size, 54), input.data); Dqn_Test_KeccakDispatch_(&test, hash_type, input); Dqn_UTest_End(&test); } - Dqn_UTest_Begin(&test, "%.*s - Deterministic random inputs", DQN_STRING_FMT(DQN_UTEST_HASH_STRING_[hash_type])); + Dqn_UTest_Begin(&test, "%.*s - Deterministic random inputs", DQN_STR_FMT(DQN_UTEST_HASH_STRING_[hash_type])); for (int index = 0; index < 128; index++) { char src[4096] = {}; uint32_t src_size = pcg32_boundedrand_r(&rng, sizeof(src)); @@ -1008,7 +1141,7 @@ Dqn_UTest Dqn_Test_Keccak() for (int src_index = 0; src_index < src_size; src_index++) src[src_index] = pcg32_boundedrand_r(&rng, 255); - Dqn_String8 input = Dqn_String8_Init(src, src_size); + Dqn_Str8 input = Dqn_Str8_Init(src, src_size); TestKeccakDispatch_(&test, hash_type, input); } Dqn_UTest_End(&test); @@ -1071,9 +1204,9 @@ static Dqn_UTest Dqn_Test_OS() DQN_UTEST_TEST("Query executable directory") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 result = Dqn_OS_EXEDir(scratch.arena); - DQN_UTEST_ASSERT(&test, Dqn_String8_IsValid(result)); - DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STRING_FMT(result)); + Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena); + DQN_UTEST_ASSERT(&test, Dqn_Str8_IsValid(result)); + DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result)); } DQN_UTEST_TEST("Dqn_OS_PerfCounterNow") { @@ -1231,12 +1364,12 @@ static Dqn_UTest Dqn_Test_Rect() return test; } -static Dqn_UTest Dqn_Test_String8() +static Dqn_UTest Dqn_Test_Str8() { Dqn_UTest test = {}; - DQN_UTEST_GROUP(test, "Dqn_String8") { + DQN_UTEST_GROUP(test, "Dqn_Str8") { DQN_UTEST_TEST("Initialise with string literal w/ macro") { - Dqn_String8 string = DQN_STRING8("AB"); + Dqn_Str8 string = DQN_STR8("AB"); DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); @@ -1244,7 +1377,7 @@ static Dqn_UTest Dqn_Test_String8() DQN_UTEST_TEST("Initialise with format string") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 string = Dqn_String8_InitF(scratch.allocator, "%s", "AB"); + Dqn_Str8 string = Dqn_Str8_InitF(scratch.allocator, "%s", "AB"); DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]); DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); @@ -1253,8 +1386,8 @@ static Dqn_UTest Dqn_Test_String8() DQN_UTEST_TEST("Copy string") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 string = DQN_STRING8("AB"); - Dqn_String8 copy = Dqn_String8_Copy(scratch.allocator, string); + Dqn_Str8 string = DQN_STR8("AB"); + Dqn_Str8 copy = Dqn_Str8_Copy(scratch.allocator, string); DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %I64u", copy.size); DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]); DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]); @@ -1262,232 +1395,232 @@ static Dqn_UTest Dqn_Test_String8() } DQN_UTEST_TEST("Trim whitespace around string") { - Dqn_String8 string = Dqn_String8_TrimWhitespaceAround(DQN_STRING8(" AB ")); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(string, DQN_STRING8("AB")), "[string=%.*s]", DQN_STRING_FMT(string)); + Dqn_Str8 string = Dqn_Str8_TrimWhitespaceAround(DQN_STR8(" AB ")); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(string, DQN_STR8("AB")), "[string=%.*s]", DQN_STR_FMT(string)); } DQN_UTEST_TEST("Allocate string from arena") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 string = Dqn_String8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No); + Dqn_Str8 string = Dqn_Str8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No); DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); } - // NOTE: Dqn_CString8_Trim[Prefix/Suffix] + // NOTE: Dqn_CStr8_Trim[Prefix/Suffix] // --------------------------------------------------------------------------------------------- DQN_UTEST_TEST("Trim prefix with matching prefix") { - Dqn_String8 input = DQN_STRING8("nft/abc"); - Dqn_String8 result = Dqn_String8_TrimPrefix(input, DQN_STRING8("nft/")); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(result, DQN_STRING8("abc")), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 input = DQN_STR8("nft/abc"); + Dqn_Str8 result = Dqn_Str8_TrimPrefix(input, DQN_STR8("nft/")); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, DQN_STR8("abc")), "%.*s", DQN_STR_FMT(result)); } DQN_UTEST_TEST("Trim prefix with non matching prefix") { - Dqn_String8 input = DQN_STRING8("nft/abc"); - Dqn_String8 result = Dqn_String8_TrimPrefix(input, DQN_STRING8(" ft/")); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(result, input), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 input = DQN_STR8("nft/abc"); + Dqn_Str8 result = Dqn_Str8_TrimPrefix(input, DQN_STR8(" ft/")); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, input), "%.*s", DQN_STR_FMT(result)); } DQN_UTEST_TEST("Trim suffix with matching suffix") { - Dqn_String8 input = DQN_STRING8("nft/abc"); - Dqn_String8 result = Dqn_String8_TrimSuffix(input, DQN_STRING8("abc")); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(result, DQN_STRING8("nft/")), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 input = DQN_STR8("nft/abc"); + Dqn_Str8 result = Dqn_Str8_TrimSuffix(input, DQN_STR8("abc")); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, DQN_STR8("nft/")), "%.*s", DQN_STR_FMT(result)); } DQN_UTEST_TEST("Trim suffix with non matching suffix") { - Dqn_String8 input = DQN_STRING8("nft/abc"); - Dqn_String8 result = Dqn_String8_TrimSuffix(input, DQN_STRING8("ab")); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(result, input), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 input = DQN_STR8("nft/abc"); + Dqn_Str8 result = Dqn_Str8_TrimSuffix(input, DQN_STR8("ab")); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, input), "%.*s", DQN_STR_FMT(result)); } - // NOTE: Dqn_String8_IsAllDigits + // NOTE: Dqn_Str8_IsAllDigits // --------------------------------------------------------------------------------------------- DQN_UTEST_TEST("Is all digits fails on non-digit string") { - Dqn_b32 result = Dqn_String8_IsAll(DQN_STRING8("@123string"), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("@123string"), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, result == false); } DQN_UTEST_TEST("Is all digits fails on nullptr") { - Dqn_b32 result = Dqn_String8_IsAll(Dqn_String8_Init(nullptr, 0), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(nullptr, 0), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, result == false); } DQN_UTEST_TEST("Is all digits fails on nullptr w/ size") { - Dqn_b32 result = Dqn_String8_IsAll(Dqn_String8_Init(nullptr, 1), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(nullptr, 1), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, result == false); } DQN_UTEST_TEST("Is all digits succeeds on string w/ 0 size") { char const buf[] = "@123string"; - Dqn_b32 result = Dqn_String8_IsAll(Dqn_String8_Init(buf, 0), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(buf, 0), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, result); } DQN_UTEST_TEST("Is all digits success") { - Dqn_b32 result = Dqn_String8_IsAll(DQN_STRING8("23"), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("23"), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, DQN_CAST(bool)result == true); } DQN_UTEST_TEST("Is all digits fails on whitespace") { - Dqn_b32 result = Dqn_String8_IsAll(DQN_STRING8("23 "), Dqn_String8IsAll_Digits); + Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("23 "), Dqn_Str8IsAll_Digits); DQN_UTEST_ASSERT(&test, DQN_CAST(bool)result == false); } - // NOTE: Dqn_String8_BinarySplit + // NOTE: Dqn_Str8_BinarySplit // --------------------------------------------------------------------------------------------- { { char const *TEST_FMT = "Binary split \"%.*s\" with \"%.*s\""; - Dqn_String8 delimiter = DQN_STRING8("/"); - Dqn_String8 input = DQN_STRING8("abcdef"); - DQN_UTEST_TEST(TEST_FMT, DQN_STRING_FMT(input), DQN_STRING_FMT(delimiter)) { - Dqn_String8BinarySplitResult split = Dqn_String8_BinarySplit(input, delimiter); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.lhs, DQN_STRING8("abcdef")), "[lhs=%.*s]", DQN_STRING_FMT(split.lhs)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.rhs, DQN_STRING8("")), "[rhs=%.*s]", DQN_STRING_FMT(split.rhs)); + Dqn_Str8 delimiter = DQN_STR8("/"); + Dqn_Str8 input = DQN_STR8("abcdef"); + DQN_UTEST_TEST(TEST_FMT, DQN_STR_FMT(input), DQN_STR_FMT(delimiter)) { + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplit(input, delimiter); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.lhs, DQN_STR8("abcdef")), "[lhs=%.*s]", DQN_STR_FMT(split.lhs)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.rhs, DQN_STR8("")), "[rhs=%.*s]", DQN_STR_FMT(split.rhs)); } - input = DQN_STRING8("abc/def"); - DQN_UTEST_TEST(TEST_FMT, DQN_STRING_FMT(input), DQN_STRING_FMT(delimiter)) { - Dqn_String8BinarySplitResult split = Dqn_String8_BinarySplit(input, delimiter); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.lhs, DQN_STRING8("abc")), "[lhs=%.*s]", DQN_STRING_FMT(split.lhs)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.rhs, DQN_STRING8("def")), "[rhs=%.*s]", DQN_STRING_FMT(split.rhs)); + input = DQN_STR8("abc/def"); + DQN_UTEST_TEST(TEST_FMT, DQN_STR_FMT(input), DQN_STR_FMT(delimiter)) { + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplit(input, delimiter); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.lhs, DQN_STR8("abc")), "[lhs=%.*s]", DQN_STR_FMT(split.lhs)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.rhs, DQN_STR8("def")), "[rhs=%.*s]", DQN_STR_FMT(split.rhs)); } - input = DQN_STRING8("/abcdef"); - DQN_UTEST_TEST(TEST_FMT, DQN_STRING_FMT(input), DQN_STRING_FMT(delimiter)) { - Dqn_String8BinarySplitResult split = Dqn_String8_BinarySplit(input, delimiter); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.lhs, DQN_STRING8("")), "[lhs=%.*s]", DQN_STRING_FMT(split.lhs)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.rhs, DQN_STRING8("abcdef")), "[rhs=%.*s]", DQN_STRING_FMT(split.rhs)); + input = DQN_STR8("/abcdef"); + DQN_UTEST_TEST(TEST_FMT, DQN_STR_FMT(input), DQN_STR_FMT(delimiter)) { + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplit(input, delimiter); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.lhs, DQN_STR8("")), "[lhs=%.*s]", DQN_STR_FMT(split.lhs)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.rhs, DQN_STR8("abcdef")), "[rhs=%.*s]", DQN_STR_FMT(split.rhs)); } } { - Dqn_String8 delimiter = DQN_STRING8("-=-"); - Dqn_String8 input = DQN_STRING8("123-=-456"); - DQN_UTEST_TEST("Binary split \"%.*s\" with \"%.*s\"", DQN_STRING_FMT(input), DQN_STRING_FMT(delimiter)) { - Dqn_String8BinarySplitResult split = Dqn_String8_BinarySplit(input, delimiter); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.lhs, DQN_STRING8("123")), "[lhs=%.*s]", DQN_STRING_FMT(split.lhs)); - DQN_UTEST_ASSERTF(&test, Dqn_String8_Eq(split.rhs, DQN_STRING8("456")), "[rhs=%.*s]", DQN_STRING_FMT(split.rhs)); + Dqn_Str8 delimiter = DQN_STR8("-=-"); + Dqn_Str8 input = DQN_STR8("123-=-456"); + DQN_UTEST_TEST("Binary split \"%.*s\" with \"%.*s\"", DQN_STR_FMT(input), DQN_STR_FMT(delimiter)) { + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplit(input, delimiter); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.lhs, DQN_STR8("123")), "[lhs=%.*s]", DQN_STR_FMT(split.lhs)); + DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(split.rhs, DQN_STR8("456")), "[rhs=%.*s]", DQN_STR_FMT(split.rhs)); } } } - // NOTE: Dqn_String8_ToI64 + // NOTE: Dqn_Str8_ToI64 // ========================================================================================= DQN_UTEST_TEST("To I64: Convert null string") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(Dqn_String8_Init(nullptr, 5), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(Dqn_Str8_Init(nullptr, 5), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERT(&test, result.value == 0); } DQN_UTEST_TEST("To I64: Convert empty string") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8(""), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8(""), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERT(&test, result.value == 0); } DQN_UTEST_TEST("To I64: Convert \"1\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1"), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("1"), 0); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERT(&test, result.value == 1); } DQN_UTEST_TEST("To I64: Convert \"-0\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("-0"), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("-0"), 0); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERT(&test, result.value == 0); } DQN_UTEST_TEST("To I64: Convert \"-1\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("-1"), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("-1"), 0); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERT(&test, result.value == -1); } DQN_UTEST_TEST("To I64: Convert \"1.2\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1.2"), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("1.2"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERT(&test, result.value == 1); } DQN_UTEST_TEST("To I64: Convert \"1,234\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1,234"), ','); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("1,234"), ','); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERT(&test, result.value == 1234); } DQN_UTEST_TEST("To I64: Convert \"1,2\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1,2"), ','); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("1,2"), ','); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERT(&test, result.value == 12); } DQN_UTEST_TEST("To I64: Convert \"12a3\"") { - Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("12a3"), 0); + Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8("12a3"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERT(&test, result.value == 12); } - // NOTE: Dqn_String8_ToU64 + // NOTE: Dqn_Str8_ToU64 // --------------------------------------------------------------------------------------------- DQN_UTEST_TEST("To U64: Convert nullptr") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(Dqn_String8_Init(nullptr, 5), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(Dqn_Str8_Init(nullptr, 5), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert empty string") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8(""), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8(""), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"1\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1"), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("1"), 0); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERTF(&test, result.value == 1, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"-0\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("-0"), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("-0"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"-1\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("-1"), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("-1"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"1.2\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1.2"), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("1.2"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 1, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"1,234\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1,234"), ','); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("1,234"), ','); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERTF(&test, result.value == 1234, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"1,2\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1,2"), ','); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("1,2"), ','); DQN_UTEST_ASSERT(&test, result.success); DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %I64u", result.value); } DQN_UTEST_TEST("To U64: Convert \"12a3\"") { - Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("12a3"), 0); + Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8("12a3"), 0); DQN_UTEST_ASSERT(&test, !result.success); DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %I64u", result.value); } - // NOTE: Dqn_String8_Find + // NOTE: Dqn_Str8_Find // ========================================================================================= DQN_UTEST_TEST("Find: String (char) is not in buffer") { - Dqn_String8 buf = DQN_STRING8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); - Dqn_String8 find = DQN_STRING8("2"); - Dqn_String8FindResult result = Dqn_String8_FindFirstString(buf, find); + Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); + Dqn_Str8 find = DQN_STR8("2"); + Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); DQN_UTEST_ASSERT(&test, result.match.data == nullptr); @@ -1495,35 +1628,35 @@ static Dqn_UTest Dqn_Test_String8() } DQN_UTEST_TEST("Find: String (char) is in buffer") { - Dqn_String8 buf = DQN_STRING8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); - Dqn_String8 find = DQN_STRING8("6"); - Dqn_String8FindResult result = Dqn_String8_FindFirstString(buf, find); + Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); + Dqn_Str8 find = DQN_STR8("6"); + Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find); DQN_UTEST_ASSERT(&test, result.found); DQN_UTEST_ASSERT(&test, result.index == 2); DQN_UTEST_ASSERT(&test, result.match.data[0] == '6'); } - // NOTE: Dqn_String8_FileNameFromPath + // NOTE: Dqn_Str8_FileNameFromPath // ========================================================================================= DQN_UTEST_TEST("File name from Windows path") { - Dqn_String8 buf = DQN_STRING8("C:\\ABC\\test.exe"); - Dqn_String8 result = Dqn_String8_FileNameFromPath(buf); - DQN_UTEST_ASSERTF(&test, result == DQN_STRING8("test.exe"), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 buf = DQN_STR8("C:\\ABC\\test.exe"); + Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf); + DQN_UTEST_ASSERTF(&test, result == DQN_STR8("test.exe"), "%.*s", DQN_STR_FMT(result)); } DQN_UTEST_TEST("File name from Linux path") { - Dqn_String8 buf = DQN_STRING8("/ABC/test.exe"); - Dqn_String8 result = Dqn_String8_FileNameFromPath(buf); - DQN_UTEST_ASSERTF(&test, result == DQN_STRING8("test.exe"), "%.*s", DQN_STRING_FMT(result)); + Dqn_Str8 buf = DQN_STR8("/ABC/test.exe"); + Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf); + DQN_UTEST_ASSERTF(&test, result == DQN_STR8("test.exe"), "%.*s", DQN_STR_FMT(result)); } - // NOTE: Dqn_String8_TrimPrefix + // NOTE: Dqn_Str8_TrimPrefix // ========================================================================================= DQN_UTEST_TEST("Trim prefix") { - Dqn_String8 prefix = DQN_STRING8("@123"); - Dqn_String8 buf = DQN_STRING8("@123string"); - Dqn_String8 result = Dqn_String8_TrimPrefix(buf, prefix, Dqn_String8EqCase_Sensitive); - DQN_UTEST_ASSERT(&test, result == DQN_STRING8("string")); + Dqn_Str8 prefix = DQN_STR8("@123"); + Dqn_Str8 buf = DQN_STR8("@123string"); + Dqn_Str8 result = Dqn_Str8_TrimPrefix(buf, prefix, Dqn_Str8EqCase_Sensitive); + DQN_UTEST_ASSERT(&test, result == DQN_STR8("string")); } } return test; @@ -1572,7 +1705,7 @@ static Dqn_UTest Dqn_Test_VArray() DQN_UTEST_TEST("Test adding an array of items to the array") { uint32_t array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - Dqn_VArray_Add(&array, array_literal, DQN_ARRAY_UCOUNT(array_literal)); + Dqn_VArray_AddArray(&array, array_literal, DQN_ARRAY_UCOUNT(array_literal)); DQN_UTEST_ASSERT(&test, array.size == DQN_ARRAY_UCOUNT(array_literal)); DQN_UTEST_ASSERT(&test, DQN_MEMCMP(array.data, array_literal, DQN_ARRAY_UCOUNT(array_literal) * sizeof(array_literal[0])) == 0); } @@ -1645,7 +1778,7 @@ static Dqn_UTest Dqn_Test_VArray() DQN_UTEST_TEST("Test adding an array of items after erase") { uint32_t array_literal[] = {0, 1, 2, 3}; - Dqn_VArray_Add(&array, array_literal, DQN_ARRAY_UCOUNT(array_literal)); + Dqn_VArray_AddArray(&array, array_literal, DQN_ARRAY_UCOUNT(array_literal)); uint32_t expected_literal[] = {14, 6, 0, 1, 2, 3}; DQN_UTEST_ASSERT(&test, array.size == DQN_ARRAY_UCOUNT(expected_literal)); @@ -1677,8 +1810,8 @@ static Dqn_UTest Dqn_Test_VArray() // NOTE: Verify that the items returned from the data array are // contiguous in memory. - UnalignedObject *make_item_a = Dqn_VArray_Make(&array, 1, Dqn_ZeroMem_Yes); - UnalignedObject *make_item_b = Dqn_VArray_Make(&array, 1, Dqn_ZeroMem_Yes); + UnalignedObject *make_item_a = Dqn_VArray_MakeArray(&array, 1, Dqn_ZeroMem_Yes); + UnalignedObject *make_item_b = Dqn_VArray_MakeArray(&array, 1, Dqn_ZeroMem_Yes); DQN_MEMSET(make_item_a->data, 'a', sizeof(make_item_a->data)); DQN_MEMSET(make_item_b->data, 'b', sizeof(make_item_b->data)); DQN_UTEST_ASSERT(&test, (uintptr_t)make_item_b == (uintptr_t)(make_item_a + 1)); @@ -1708,36 +1841,36 @@ static Dqn_UTest Dqn_Test_Win() { Dqn_UTest test = {}; DQN_UTEST_GROUP(test, "Dqn_Win") { - DQN_UTEST_TEST("String8 to String16 size required") { - int result = Dqn_Win_String8ToString16Buffer(DQN_STRING8("a"), nullptr, 0); + DQN_UTEST_TEST("Str8 to Str16 size required") { + int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("a"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result); } - DQN_UTEST_TEST("String16 to String8 size required") { - int result = Dqn_Win_String16ToString8Buffer(DQN_STRING16(L"a"), nullptr, 0); + DQN_UTEST_TEST("Str16 to Str8 size required") { + int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"a"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result); } - DQN_UTEST_TEST("String8 to String16 size required") { - int result = Dqn_Win_String8ToString16Buffer(DQN_STRING8("String"), nullptr, 0); + DQN_UTEST_TEST("Str8 to Str16 size required") { + int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("String"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result); } - DQN_UTEST_TEST("String16 to String8 size required") { - int result = Dqn_Win_String16ToString8Buffer(DQN_STRING16(L"String"), nullptr, 0); + DQN_UTEST_TEST("Str16 to Str8 size required") { + int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"String"), nullptr, 0); DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result); } - DQN_UTEST_TEST("String8 to String16") { + DQN_UTEST_TEST("Str8 to Str16") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 const INPUT = DQN_STRING8("String"); - int size_required = Dqn_Win_String8ToString16Buffer(INPUT, nullptr, 0); + Dqn_Str8 const INPUT = DQN_STR8("String"); + int size_required = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0); wchar_t *string = Dqn_Arena_NewArray(scratch.arena, wchar_t, size_required + 1, Dqn_ZeroMem_No); // Fill the string with error sentinels, which ensures the string is zero terminated DQN_MEMSET(string, 'Z', size_required + 1); - int size_returned = Dqn_Win_String8ToString16Buffer(INPUT, string, size_required + 1); + int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, string, size_required + 1); wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0}; DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); @@ -1745,16 +1878,16 @@ static Dqn_UTest Dqn_Test_Win() DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0); } - DQN_UTEST_TEST("String16 to String8: No null-terminate") { + DQN_UTEST_TEST("Str16 to Str8: No null-terminate") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 INPUT = DQN_STRING16(L"String"); - int size_required = Dqn_Win_String16ToString8Buffer(INPUT, nullptr, 0); + Dqn_Str16 INPUT = DQN_STR16(L"String"); + int size_required = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0); char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No); // Fill the string with error sentinels, which ensures the string is zero terminated DQN_MEMSET(string, 'Z', size_required + 1); - int size_returned = Dqn_Win_String16ToString8Buffer(INPUT, string, size_required + 1); + int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, string, size_required + 1); char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); @@ -1762,12 +1895,12 @@ static Dqn_UTest Dqn_Test_Win() DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0); } - DQN_UTEST_TEST("String8 to String16 arena") { + DQN_UTEST_TEST("Str8 to Str16 arena") { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 const INPUT = DQN_STRING8("String"); - Dqn_String16 string16 = Dqn_Win_String8ToString16(scratch.arena, INPUT); + Dqn_Str8 const INPUT = DQN_STR8("String"); + Dqn_Str16 string16 = Dqn_Win_Str8ToStr16(scratch.arena, INPUT); - int size_returned = Dqn_Win_String8ToString16Buffer(INPUT, nullptr, 0); + int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0); wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0}; DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string16.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string16.size, size_returned); @@ -1775,12 +1908,12 @@ static Dqn_UTest Dqn_Test_Win() DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string16.data, sizeof(EXPECTED)) == 0); } - DQN_UTEST_TEST("String16 to String8: No null-terminate arena") { + DQN_UTEST_TEST("Str16 to Str8: 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); + Dqn_Str16 INPUT = DQN_STR16(L"String"); + Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, INPUT); - int size_returned = Dqn_Win_String16ToString8Buffer(INPUT, nullptr, 0); + int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0); char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string8.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string8.size, size_returned); @@ -1791,17 +1924,17 @@ static Dqn_UTest Dqn_Test_Win() return test; } -static void Dqn_Test_CustomLogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args) +static void Dqn_Test_CustomLogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args) { (void)user_data; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 log = Dqn_Log_MakeString(scratch.allocator, true /*colour*/, type, log_type, call_site, fmt, args); - DQN_UTEST_LOG("%.*s", DQN_STRING_FMT(log)); + Dqn_Str8 log = Dqn_Log_MakeStr8(scratch.allocator, true /*colour*/, type, log_type, call_site, fmt, args); + DQN_UTEST_LOG("%.*s", DQN_STR_FMT(log)); } static void Dqn_Test_RunSuite() { - Dqn_Library *dqn_library = Dqn_Library_Init(); + Dqn_Library *dqn_library = Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures); auto *prev_log_callback = dqn_library->log_callback; dqn_library->log_callback = Dqn_Test_CustomLogProc; @@ -1811,7 +1944,7 @@ static void Dqn_Test_RunSuite() Dqn_Test_Bin(), Dqn_Test_BinarySearch(), Dqn_Test_DSMap(), - Dqn_Test_FString8(), + Dqn_Test_FStr8(), Dqn_Test_Fs(), Dqn_Test_FixedArray(), Dqn_Test_Intrinsics(), @@ -1821,7 +1954,7 @@ static void Dqn_Test_RunSuite() Dqn_Test_M4(), Dqn_Test_OS(), Dqn_Test_Rect(), - Dqn_Test_String8(), + Dqn_Test_Str8(), Dqn_Test_TicketMutex(), Dqn_Test_VArray(), Dqn_Test_Win(), @@ -1842,7 +1975,7 @@ static void Dqn_Test_RunSuite() int main(int argc, char *argv[]) { (void)argv; (void)argc; - Dqn_Library_Init(); + Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures); Dqn_Test_RunSuite(); return 0; } diff --git a/build.bat b/build.bat index 87b0d2d..8f323ef 100644 --- a/build.bat +++ b/build.bat @@ -19,7 +19,7 @@ pushd Build REM Optionally pass `-analyze` to `msvc_compile_flags` for more checks, but, REM it slows down compilation by around 5s on my old laptop. - set msvc_compile_flags=%msvc_driver_flags% -fsanitize=address /Fe:dqn_unit_tests_msvc + set msvc_compile_flags=%msvc_driver_flags% -analyze -fsanitize=address /Fe:dqn_unit_tests_msvc set clang_compile_flags=%msvc_driver_flags% -fsanitize=address -fsanitize=undefined /Fe:dqn_unit_tests_clang set zig_compile_flags=%common_flags% -fsanitize=address -fsanitize=undefined -o dqn_unit_tests_zig diff --git a/dqn.h b/dqn.h index c06bcec..deb26cd 100644 --- a/dqn.h +++ b/dqn.h @@ -41,7 +41,7 @@ defined(DQN_ONLY_SLICE) || \ defined(DQN_ONLY_DSMAP) || \ defined(DQN_ONLY_LIST) || \ - defined(DQN_ONLY_FSTRING8) || \ + defined(DQN_ONLY_FSTR8) || \ defined(DQN_ONLY_FS) || \ defined(DQN_ONLY_WINNET) || \ defined(DQN_ONLY_WIN) || \ @@ -72,8 +72,8 @@ #if !defined(DQN_ONLY_LIST) #define DQN_NO_LIST #endif - #if !defined(DQN_ONLY_FSTRING8) - #define DQN_NO_FSTRING8 + #if !defined(DQN_ONLY_FSTR8) + #define DQN_NO_FSTR8 #endif #if !defined(DQN_ONLY_FS) #define DQN_NO_FS @@ -202,11 +202,18 @@ // [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog #include "dqn_memory.h" +// NOTE: Dqn_Debug ================================================================================= +// [$DEBM] Debug Macros | | +// [$ASAN] Dqn_Asan | | Helpers to manually poison memory using ASAN +// [$STKT] Dqn_StackTrace | | Create stack traces +// [$DEBG] Dqn_Debug | | Debugging tools/helpers +#include "dqn_debug.h" + // NOTE: Dqn_Strings =============================================================================== -// [$CSTR] Dqn_CString8 | | C-string helpers -// [$STR8] Dqn_String8 | | Pointer and length strings -// [$FSTR] Dqn_FString8 | DQN_FSTRING8 | Fixed-size strings -// [$STRB] Dqn_String8Builder | | +// [$CSTR] Dqn_CStr8 | | C-string helpers +// [$STR8] Dqn_Str8 | | Pointer and length strings +// [$FSTR] Dqn_FStr8 | DQN_FSTr8 | Fixed-size strings +// [$STRB] Dqn_Str8Builder | | // [$CHAR] Dqn_Char | | Character ascii/digit.. helpers // [$UTFX] Dqn_UTF | | Unicode helpers #include "dqn_strings.h" @@ -221,13 +228,6 @@ // [$LIST] Dqn_List | DQN_LIST | Chunked linked lists, append only #include "dqn_containers.h" -// NOTE: Dqn_Debug ================================================================================= -// [$DEBM] Debug Macros | | -// [$ASAN] Dqn_Asan | | Helpers to manually poison memory using ASAN -// [$STKT] Dqn_StackTrace | | Create stack traces -// [$DEBG] Dqn_Debug | | Debugging tools/helpers -#include "dqn_debug.h" - // NOTE: Additional Configuration // - Override the default break into the active debugger function. By default // we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms. @@ -285,6 +285,10 @@ // [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas #include "dqn_platform.h" +// NOTE: Dqn_OS ==================================================================================== +// [$EXEC] Dqn_OSExec | | Execute programs programatically +#include "dqn_os.h" + // NOTE: Dqn_Math ================================================================================== // [$VEC2] Dqn_V2, V2i | DQN_V2 | // [$VEC3] Dqn_V3, V3i | DQN_V3 | @@ -300,6 +304,7 @@ #include "dqn_hash.h" // NOTE: Dqn_Helpers =============================================================================== +// [$PCG3] Dqn_PCG32 | | RNG from the PCG family // [$JSON] Dqn_JSONBuilder | DQN_JSON_BUILDER | Construct json output // [$BHEX] Dqn_Bin | DQN_BIN | Binary <-> hex helpers // [$BSEA] Dqn_BinarySearch | | Binary search @@ -319,6 +324,7 @@ #include "dqn_strings.cpp" #include "dqn_containers.cpp" #include "dqn_platform.cpp" +#include "dqn_os.cpp" #include "dqn_math.cpp" #include "dqn_hash.cpp" #include "dqn_helpers.cpp" diff --git a/dqn_base.cpp b/dqn_base.cpp index 4effe55..d57dd54 100644 --- a/dqn_base.cpp +++ b/dqn_base.cpp @@ -1,5 +1,5 @@ // NOTE: [$INTR] Intrinsics ======================================================================== -#if !defined(DQN_OS_ARM64) +#if !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN) #if defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) #include #endif @@ -16,7 +16,7 @@ Dqn_CPUIDRegisters Dqn_CPUID(int function_id) #endif return result; } -#endif // !defined(DQN_OS_ARM64) +#endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN) // NOTE: [$ALLO] Dqn_Allocator ===================================================================== DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem) @@ -110,7 +110,7 @@ DQN_API Dqn_PrintStyle Dqn_Print_StyleBold() return result; } -DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string) +DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string) { DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err); @@ -147,26 +147,26 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string) WriteFile(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr); } #else - fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STRING_FMT(string)); + fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STR_FMT(string)); #endif } -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_Str8 string) { if (string.data && string.size) { if (style.colour) - Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b)); + Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgStr8(style.r, style.g, style.b)); if (style.bold == Dqn_PrintBold_Yes) - Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString); + Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldStr8); Dqn_Print_Std(std_handle, string); if (style.colour || style.bold == Dqn_PrintBold_Yes) - Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString); + Dqn_Print_Std(std_handle, Dqn_Print_ESCResetStr8); } } DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len) { - Dqn_String8 string = {}; + Dqn_Str8 string = {}; string.data = DQN_CAST(char *)buf; string.size = len; @@ -175,7 +175,7 @@ DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int return (char *)buf; } -DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -183,7 +183,7 @@ DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE cha va_end(args); } -DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -191,32 +191,32 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, va_end(args); } -DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args) { char buffer[STB_SPRINTF_MIN]; STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args); } -DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args) { if (fmt) { if (style.colour) - Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b)); + Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgStr8(style.r, style.g, style.b)); if (style.bold == Dqn_PrintBold_Yes) - Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString); + Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldStr8); Dqn_Print_StdFV(std_handle, fmt, args); if (style.colour || style.bold == Dqn_PrintBold_Yes) - Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString); + Dqn_Print_Std(std_handle, Dqn_Print_ESCResetStr8); } } -DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string) +DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_Str8 string) { Dqn_Print_Std(std_handle, string); - Dqn_Print_Std(std_handle, DQN_STRING8("\n")); + Dqn_Print_Std(std_handle, DQN_STR8("\n")); } -DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -224,19 +224,19 @@ DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE c va_end(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_StdLnFV(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args) { Dqn_Print_StdFV(std_handle, fmt, args); - Dqn_Print_Std(std_handle, DQN_STRING8("\n")); + Dqn_Print_Std(std_handle, DQN_STR8("\n")); } -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_Str8 string) { Dqn_Print_StdStyle(std_handle, style, string); - Dqn_Print_Std(std_handle, DQN_STRING8("\n")); + Dqn_Print_Std(std_handle, DQN_STR8("\n")); } -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_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -244,87 +244,87 @@ DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style va_end(args); } -DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args) { Dqn_Print_StdFVStyle(std_handle, style, fmt, args); - Dqn_Print_Std(std_handle, DQN_STRING8("\n")); + Dqn_Print_Std(std_handle, DQN_STR8("\n")); } -DQN_API Dqn_String8 Dqn_Print_ESCColourString(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b) +DQN_API Dqn_Str8 Dqn_Print_ESCColourStr8(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b) { DQN_THREAD_LOCAL char buffer[32]; - buffer[0] = 0; - Dqn_String8 result = {}; - result.size = STB_SPRINTF_DECORATE(snprintf)(buffer, - DQN_ARRAY_UCOUNT(buffer), - "\x1b[%d;2;%u;%u;%um", - colour == Dqn_PrintESCColour_Fg ? 38 : 48, - r, g, b); - result.data = buffer; + buffer[0] = 0; + Dqn_Str8 result = {}; + result.size = STB_SPRINTF_DECORATE(snprintf)(buffer, + DQN_ARRAY_UCOUNT(buffer), + "\x1b[%d;2;%u;%u;%um", + colour == Dqn_PrintESCColour_Fg ? 38 : 48, + r, g, b); + result.data = buffer; return result; } -DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value) +DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t value) { uint8_t r = DQN_CAST(uint8_t)(value >> 24); uint8_t g = DQN_CAST(uint8_t)(value >> 16); uint8_t b = DQN_CAST(uint8_t)(value >> 8); - Dqn_String8 result = Dqn_Print_ESCColourString(colour, r, g, b); + Dqn_Str8 result = Dqn_Print_ESCColourStr8(colour, r, g, b); return result; } // NOTE: [$LLOG] Dqn_Log ========================================================================== -DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator, - bool colour, - Dqn_String8 type, - int log_type, - Dqn_CallSite call_site, - char const *fmt, - va_list args) +DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator, + bool colour, + Dqn_Str8 type, + int log_type, + Dqn_CallSite call_site, + DQN_FMT_ATTRIB char const *fmt, + va_list args) { - Dqn_usize header_size_no_ansi_codes = 0; - Dqn_String8 header = {}; + Dqn_usize header_size_no_ansi_codes = 0; + Dqn_Str8 header = {}; { DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0; max_type_length = DQN_MAX(max_type_length, type.size); int type_padding = DQN_CAST(int)(max_type_length - type.size); - Dqn_String8 colour_esc = {}; - Dqn_String8 bold_esc = {}; - Dqn_String8 reset_esc = {}; + Dqn_Str8 colour_esc = {}; + Dqn_Str8 bold_esc = {}; + Dqn_Str8 reset_esc = {}; if (colour) { - bold_esc = Dqn_Print_ESCBoldString; - reset_esc = Dqn_Print_ESCResetString; + bold_esc = Dqn_Print_ESCBoldStr8; + reset_esc = Dqn_Print_ESCResetStr8; switch (log_type) { - case Dqn_LogType_Debug: break; - case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break; - case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break; - case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break; + case Dqn_LogType_Debug: break; + case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Info); break; + case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Warning); break; + case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Error); break; } } - Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file); - Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow(); - header = Dqn_String8_InitF(allocator, - "%.*s " // date - "%.*s " // hms - "%.*s" // colour - "%.*s" // bold - "%.*s" // type - "%*s" // type padding - "%.*s" // reset - " %.*s" // file name - ":%05u ", // line number - DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date - DQN_CAST(uint32_t)time.hms_size, time.hms, // hms - DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour - DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold - DQN_CAST(uint32_t)type.size, type.data, // type - DQN_CAST(uint32_t)type_padding, "", // type padding - DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset - DQN_CAST(uint32_t)file_name.size, file_name.data, // file name - call_site.line); // line number - header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size; + Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file); + Dqn_DateHMSTimeStr8 const time = Dqn_Date_LocalTimeHMSStr8Now(); + header = Dqn_Str8_InitF(allocator, + "%.*s " // date + "%.*s " // hms + "%.*s" // colour + "%.*s" // bold + "%.*s" // type + "%*s" // type padding + "%.*s" // reset + " %.*s" // file name + ":%05u ", // line number + DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date + DQN_CAST(uint32_t)time.hms_size, time.hms, // hms + DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour + DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold + DQN_CAST(uint32_t)type.size, type.data, // type + DQN_CAST(uint32_t)type_padding, "", // type padding + DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset + DQN_CAST(uint32_t)file_name.size, file_name.data, // file name + call_site.line); // line number + header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size; } // NOTE: Header padding ======================================================================== @@ -336,15 +336,15 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator, } // NOTE: Construct final log =================================================================== - Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args); - Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No); + Dqn_Str8 user_msg = Dqn_Str8_InitFV(allocator, fmt, args); + Dqn_Str8 result = Dqn_Str8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No); DQN_MEMCPY(result.data, header.data, header.size); DQN_MEMSET(result.data + header.size, ' ', header_padding); DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size); return result; } -DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args) +DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args) { Dqn_Library *lib = g_dqn_library; (void)log_type; @@ -354,37 +354,36 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use Dqn_TicketMutex_Begin(&lib->log_file_mutex); if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STRING_FMT(lib->exe_dir)); + Dqn_Str8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir)); lib->log_file = Dqn_Fs_OpenFile(log_path, Dqn_FsFileOpen_CreateAlways, Dqn_FsFileAccess_AppendOnly); } Dqn_TicketMutex_End(&lib->log_file_mutex); // NOTE: Generate the log header =========================================== - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator, - !lib->log_no_colour, - type, - log_type, - call_site, - fmt, - args); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.allocator, !lib->log_no_colour, type, log_type, call_site, fmt, args); // NOTE: Print log ========================================================= Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line); Dqn_TicketMutex_Begin(&lib->log_file_mutex); Dqn_Fs_WriteFile(&lib->log_file, log_line); - Dqn_Fs_WriteFile(&lib->log_file, DQN_STRING8("\n")); + Dqn_Fs_WriteFile(&lib->log_file, DQN_STR8("\n")); Dqn_TicketMutex_End(&lib->log_file_mutex); } -DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args) +DQN_API void Dqn_Log_FVCallSite(Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args) { - Dqn_LogProc *logging_function = g_dqn_library->log_callback ? g_dqn_library->log_callback : Dqn_Log_FVDefault_; - logging_function(type, -1 /*log_type*/, g_dqn_library->log_user_data, call_site, fmt, args); + if (g_dqn_library) { + Dqn_LogProc *logging_function = g_dqn_library->log_callback ? g_dqn_library->log_callback : Dqn_Log_FVDefault_; + logging_function(type, -1 /*log_type*/, g_dqn_library->log_user_data, call_site, fmt, args); + } else { + // NOTE: Rarely taken branch, only when trying to use this library without initialising it + Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args); + } } -DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...) +DQN_API void Dqn_Log_FCallSite(Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -392,26 +391,23 @@ DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char co va_end(args); } -DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list args) +DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args) { - Dqn_String8 type_string = DQN_STRING8("DQN-BAD-LOG-TYPE"); + Dqn_Str8 type_string = DQN_STR8("DQN-BAD-LOG-TYPE"); switch (type) { - case Dqn_LogType_Error: type_string = DQN_STRING8("ERROR"); break; - case Dqn_LogType_Info: type_string = DQN_STRING8("INFO"); break; - case Dqn_LogType_Warning: type_string = DQN_STRING8("WARN"); break; - case Dqn_LogType_Debug: type_string = DQN_STRING8("DEBUG"); break; - case Dqn_LogType_Count: type_string = DQN_STRING8("BADXX"); break; + case Dqn_LogType_Error: type_string = DQN_STR8("ERROR"); break; + case Dqn_LogType_Info: type_string = DQN_STR8("INFO"); break; + case Dqn_LogType_Warning: type_string = DQN_STR8("WARN"); break; + case Dqn_LogType_Debug: type_string = DQN_STR8("DEBUG"); break; + case Dqn_LogType_Count: type_string = DQN_STR8("BADXX"); break; } - - Dqn_LogProc *logging_function = g_dqn_library->log_callback ? g_dqn_library->log_callback : Dqn_Log_FVDefault_; - logging_function(type_string, type /*log_type*/, g_dqn_library->log_user_data, call_site, fmt, args); + Dqn_Log_FVCallSite(type_string, call_site, fmt, args); } -DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...) +DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); Dqn_Log_TypeFVCallSite(type, call_site, fmt, args); va_end(args); } - diff --git a/dqn_base.h b/dqn_base.h index 996936f..c866675 100644 --- a/dqn_base.h +++ b/dqn_base.h @@ -39,12 +39,16 @@ #if defined(_WIN32) #define DQN_OS_WIN32 -#elif defined(__aarch64__) || defined(_M_ARM64) - #define DQN_OS_ARM64 #elif defined(__linux__) #define DQN_OS_UNIX #endif +#if defined(__aarch64__) || defined(_M_ARM64) + #define DQN_PLATFORM_ARM64 +#elif defined(__EMSCRIPTEN__) + #define DQN_PLATFORM_EMSCRIPTEN +#endif + #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(_CRT_SECURE_NO_WARNINGS) #define DQN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED @@ -54,12 +58,12 @@ #endif #if defined(DQN_COMPILER_MSVC) - #define DQN_FMT_STRING_ANNOTATE _Printf_format_string_ + #define DQN_FMT_ATTRIB _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_FMT_ATTRIB #define DQN_MSVC_WARNING_PUSH #define DQN_MSVC_WARNING_DISABLE(...) #define DQN_MSVC_WARNING_POP @@ -67,8 +71,9 @@ #if defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG_CL) #define DQN_GCC_WARNING_PUSH _Pragma("GCC diagnostic push") - #define DQN_GCC_WARNING_DISABLE(warning) DQN_GCC_WARNING_DISABLE_HELPER(GCC diagnostic ignored warning) - #define DQN_GCC_WARNING_DISABLE_HELPER(warning) _Pragma(#warning) + #define DQN_GCC_WARNING_DISABLE_HELPER_0(x) #x + #define DQN_GCC_WARNING_DISABLE_HELPER_1(y) DQN_GCC_WARNING_DISABLE_HELPER_0(GCC diagnostic ignored #y) + #define DQN_GCC_WARNING_DISABLE(warning) _Pragma(DQN_GCC_WARNING_DISABLE_HELPER_1(warning)) #define DQN_GCC_WARNING_POP _Pragma("GCC diagnostic pop") #else #define DQN_GCC_WARNING_PUSH @@ -87,11 +92,19 @@ // NOTE: Alloc Macros ============================================================================== #if !defined(DQN_ALLOC) - #define DQN_ALLOC(size) Dqn_VMem_Reserve(size, Dqn_VMemCommit_Yes, Dqn_VMemPage_ReadWrite) + #if defined(DQN_PLATFORM_EMSCRIPTEN) + #define DQN_ALLOC(size) malloc(size) + #else + #define DQN_ALLOC(size) Dqn_VMem_Reserve(size, Dqn_VMemCommit_Yes, Dqn_VMemPage_ReadWrite) + #endif #endif #if !defined(DQN_DEALLOC) - #define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size) + #if defined(DQN_PLATFORM_EMSCRIPTEN) + #define DQN_DEALLOC(ptr, size) free(ptr) + #else + #define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size) + #endif #endif // NOTE: String.h Dependencies ===================================================================== @@ -126,6 +139,10 @@ #endif #endif +#if !defined(DQN_OS_WIN32) +#include // exit() +#endif + // NOTE: Math Macros =============================================================================== #define DQN_PI 3.14159265359f @@ -206,11 +223,11 @@ // NOTE: Assert Macros ============================================================================= #define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "") -#define DQN_HARD_ASSERTF(expr, fmt, ...) \ - if (!(expr)) { \ - Dqn_Log_ErrorF("Hard assert triggered " #expr ". " fmt, ##__VA_ARGS__); \ - Dqn_StackTrace_Print(128 /*limit*/); \ - DQN_DEBUG_BREAK; \ +#define DQN_HARD_ASSERTF(expr, fmt, ...) \ + if (!(expr)) { \ + Dqn_Log_ErrorF("Hard assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \ + Dqn_StackTrace_Print(128 /*limit*/); \ + DQN_DEBUG_BREAK; \ } #if defined(DQN_NO_ASSERT) @@ -218,11 +235,11 @@ #define DQN_ASSERT(...) #else #define DQN_ASSERT(expr) DQN_ASSERTF(expr, "") - #define DQN_ASSERTF(expr, fmt, ...) \ - if (!(expr)) { \ - Dqn_Log_ErrorF("Assert triggered " #expr ". " fmt, ##__VA_ARGS__); \ - Dqn_StackTrace_Print(128 /*limit*/); \ - DQN_DEBUG_BREAK; \ + #define DQN_ASSERTF(expr, fmt, ...) \ + if (!(expr)) { \ + Dqn_Log_ErrorF("Assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \ + Dqn_StackTrace_Print(128 /*limit*/); \ + DQN_DEBUG_BREAK; \ } #endif @@ -308,6 +325,10 @@ typedef double Dqn_f64; typedef unsigned int Dqn_uint; typedef int32_t Dqn_b32; +#define DQN_F32_MAX 3.402823466e+38F +#define DQN_F32_MIN 1.175494351e-38F +#define DQN_F64_MAX 1.7976931348623158e+308 +#define DQN_F64_MIN 2.2250738585072014e-308 #define DQN_USIZE_MAX UINTPTR_MAX #define DQN_ISIZE_MAX INTPTR_MAX #define DQN_ISIZE_MIN INTPTR_MIN @@ -318,7 +339,7 @@ enum Dqn_ZeroMem Dqn_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee }; -struct Dqn_String8 +struct Dqn_Str8 { char *data; // The bytes of the string Dqn_usize size; // The number of bytes in the string @@ -329,14 +350,27 @@ struct Dqn_String8 char *end () { return data + size; } }; +#if !defined(DQN_NO_SLICE) +template struct Dqn_Slice // A pointer and length container of data +{ + T *data; + Dqn_usize size; + + T *begin() { return data; } + T *end () { return data + size; } + T const *begin() const { return data; } + T const *end () const { return data + size; } +}; +#endif + // NOTE: [$CALL] Dqn_CallSite ====================================================================== struct Dqn_CallSite { - Dqn_String8 file; - Dqn_String8 function; + Dqn_Str8 file; + Dqn_Str8 function; unsigned int line; }; -#define DQN_CALL_SITE Dqn_CallSite{DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__} +#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__} // NOTE: [$INTR] Intrinsics ======================================================================== // Platform agnostic functions for CPU level instructions like atomics, barriers @@ -367,9 +401,12 @@ struct Dqn_CallSite #define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence() #elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) #if defined(__ANDROID__) + #elif defined(DQN_PLATFORM_EMSCRIPTEN) + #include #else #include #endif + #define Dqn_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) #define Dqn_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) #define Dqn_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL) @@ -417,14 +454,14 @@ DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value) #endif } -#if !defined(DQN_OS_ARM64) +#if !defined(DQN_PLATFORM_ARM64) struct Dqn_CPUIDRegisters { Dqn_uint array[4]; ///< Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX) }; Dqn_CPUIDRegisters Dqn_CPUID(int function_id); -#endif // DQN_OS_ARM64 +#endif // DQN_PLATFORM_ARM64 // NOTE: [$TMUT] Dqn_TicketMutex =================================================================== // @@ -562,40 +599,40 @@ DQN_API Dqn_PrintStyle Dqn_Print_StyleBold (); // NOTE: Print ===================================================================================== -DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string); -DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); +DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_Str8 string); +DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args); -DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); -DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); +DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string); +DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args); -DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string); -DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); +DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_Str8 string); +DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args); -DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string); -DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); +DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string); +DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args); // NOTE: ANSI Formatting Codes ===================================================================== -Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b); -Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value); +Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b); +Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t value); -#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b) -#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b) -#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data -#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data +#define Dqn_Print_ESCColourFgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b) +#define Dqn_Print_ESCColourBgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b) +#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b).data +#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b).data -#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value) -#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value) -#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data -#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data +#define Dqn_Print_ESCColourFgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value) +#define Dqn_Print_ESCColourBgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value) +#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value).data +#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value).data -#define Dqn_Print_ESCReset "\x1b[0m" -#define Dqn_Print_ESCBold "\x1b[1m" -#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset) -#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold) +#define Dqn_Print_ESCReset "\x1b[0m" +#define Dqn_Print_ESCBold "\x1b[1m" +#define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset) +#define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold) // NOTE: [$LLOG] Dqn_Log ========================================================================== // NOTE: API @@ -623,7 +660,7 @@ enum Dqn_LogType #define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow #define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red -typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list va); +typedef void Dqn_LogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va); #define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__) #define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__) @@ -641,8 +678,8 @@ typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_Ca #define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args) #define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__) -DQN_API Dqn_String8 Dqn_Log_MakeString (Dqn_Allocator allocator, bool colour, Dqn_String8 type, int log_type, Dqn_CallSite call_site, char const *fmt, va_list args); -DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list va); -DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API void Dqn_Log_FVCallSite (Dqn_String8 type, Dqn_CallSite call_site, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list va); -DQN_API void Dqn_Log_FCallSite (Dqn_String8 type, Dqn_CallSite call_site, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API Dqn_Str8 Dqn_Log_MakeStr8 (Dqn_Allocator allocator, bool colour, Dqn_Str8 type, int log_type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va); +DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va); +DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...); diff --git a/dqn_containers.h b/dqn_containers.h index 0a1dab2..46bf74c 100644 --- a/dqn_containers.h +++ b/dqn_containers.h @@ -5,8 +5,32 @@ enum Dqn_ArrayErase Dqn_ArrayErase_Stable, }; -template Dqn_usize Dqn_CArray_EraseRange(T* data, Dqn_usize *size, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); -template T * Dqn_CArray_Make (T* data, Dqn_usize *size, Dqn_usize max, Dqn_usize count, Dqn_ZeroMem zero_mem); +struct Dqn_ArrayEraseResult +{ + // The next index your for-index should be set to such that you can continue + // to iterate the remainder of the array, e.g: + // + // for (Dqn_usize index = 0; index < array.size; index++) { + // if (erase) + // index = Dqn_FArray_EraseRange(&array, index, -3, Dqn_ArrayErase_Unstable); + // } + Dqn_usize it_index; + Dqn_usize items_erased; // The number of items erased +}; + +template +struct Dqn_ArrayFindResult +{ + T *data; // Pointer to the value if a match is found, null pointer otherwise + Dqn_usize index; // Index to the value if a match is found, 0 otherwise +}; + +template Dqn_ArrayEraseResult Dqn_CArray_EraseRange (T *data, Dqn_usize *size, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); +template T * Dqn_CArray_MakeArray (T *data, Dqn_usize *size, Dqn_usize max, Dqn_usize count, Dqn_ZeroMem zero_mem); +template T * Dqn_CArray_InsertArray(T *data, Dqn_usize *size, Dqn_usize max, Dqn_usize index, T const *items, Dqn_usize count); +template T Dqn_CArray_PopFront (T *data, Dqn_usize *size, Dqn_usize count); +template T Dqn_CArray_PopBack (T *data, Dqn_usize *size, Dqn_usize count); +template Dqn_ArrayFindResult Dqn_CArray_Find (T *data, Dqn_usize size, T const &value); #if !defined(DQN_NO_VARRAY) // NOTE: [$VARR] Dqn_VArray ======================================================================== @@ -85,18 +109,22 @@ template struct Dqn_VArray }; // NOTE: Setup ===================================================================================== -DQN_API template Dqn_VArray Dqn_VArray_InitByteSize(Dqn_Arena *arena, Dqn_usize byte_size); -DQN_API template Dqn_VArray Dqn_VArray_Init (Dqn_Arena *arena, Dqn_usize max); -DQN_API template bool Dqn_VArray_IsValid (Dqn_VArray const *array); -DQN_API template void Dqn_VArray_Reserve (Dqn_VArray *array, Dqn_usize count); +DQN_API template Dqn_VArray Dqn_VArray_InitByteSize(Dqn_Arena *arena, Dqn_usize byte_size); +DQN_API template Dqn_VArray Dqn_VArray_Init (Dqn_Arena *arena, Dqn_usize max); +DQN_API template bool Dqn_VArray_IsValid (Dqn_VArray const *array); +DQN_API template void Dqn_VArray_Reserve (Dqn_VArray *array, Dqn_usize count); // NOTE: Insert ==================================================================================== -DQN_API template T * Dqn_VArray_Make (Dqn_VArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); -DQN_API template T * Dqn_VArray_Add (Dqn_VArray *array, T const *items, Dqn_usize count); +DQN_API template T * Dqn_VArray_MakeArray (Dqn_VArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_VArray_Make (Dqn_VArray *array, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_VArray_AddArray (Dqn_VArray *array, T const *items, Dqn_usize count); +DQN_API template T * Dqn_VArray_Add (Dqn_VArray *array, T const &item); +DQN_API template T Dqn_VArray_PopFront (Dqn_VArray *array, Dqn_usize count); +DQN_API template T Dqn_VArray_PopBack (Dqn_VArray *array, Dqn_usize count); // NOTE: Modify ==================================================================================== -DQN_API template void Dqn_VArray_EraseRange (Dqn_VArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); -DQN_API template void Dqn_VArray_Clear (Dqn_VArray *array); +DQN_API template Dqn_ArrayEraseResult Dqn_VArray_EraseRange (Dqn_VArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); +DQN_API template void Dqn_VArray_Clear (Dqn_VArray *array, Dqn_ZeroMem zero_mem); #endif // !defined(DQN_NO_VARRAY) #if !defined(DQN_NO_SARRAY) @@ -114,17 +142,21 @@ template struct Dqn_SArray }; // NOTE: Setup ===================================================================================== -DQN_API template Dqn_SArray Dqn_SArray_Init (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); -DQN_API template bool Dqn_SArray_IsValid (Dqn_SArray const *array); +DQN_API template Dqn_SArray Dqn_SArray_Init (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); +DQN_API template Dqn_SArray Dqn_SArray_InitCArrayCopy (Dqn_Arena *arena, T const (&array)[N]); +DQN_API template bool Dqn_SArray_IsValid (Dqn_SArray const *array); -// NOTE: Insert ==================================================================================== -DQN_API template T * Dqn_SArray_Make (Dqn_SArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); -DQN_API template T * Dqn_SArray_AddArray(Dqn_SArray *array, T const *items, Dqn_usize count); -DQN_API template T * Dqn_SArray_Add (Dqn_SArray *array, T const &item); - -// NOTE: Modify ==================================================================================== -DQN_API template void Dqn_SArray_EraseRange(Dqn_SArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); -DQN_API template void Dqn_SArray_Clear (Dqn_SArray *array); +// NOTE: API ======================================================================================= +DQN_API template T * Dqn_SArray_MakeArray (Dqn_SArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_SArray_Make (Dqn_SArray *array, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_SArray_AddArray (Dqn_SArray *array, T const *items, Dqn_usize count); +DQN_API template T * Dqn_SArray_Add (Dqn_SArray *array, T const &item); +DQN_API template T * Dqn_SArray_InsertArray(Dqn_SArray *array, Dqn_usize index, T const *items, Dqn_usize count); +DQN_API template T * Dqn_SArray_Insert (Dqn_SArray *array, Dqn_usize index, T const &item); +DQN_API template T Dqn_SArray_PopFront (Dqn_SArray *array, Dqn_usize count); +DQN_API template T Dqn_SArray_PopBack (Dqn_SArray *array, Dqn_usize count); +DQN_API template Dqn_ArrayEraseResult Dqn_SArray_EraseRange (Dqn_SArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); +DQN_API template void Dqn_SArray_Clear (Dqn_SArray *array); #endif // !defined(DQN_NO_SARRAY) #if !defined(DQN_NO_FARRAY) @@ -141,35 +173,30 @@ template struct Dqn_FArray }; // NOTE: Setup ===================================================================================== -DQN_API template Dqn_FArray Dqn_FArray_Init (T const *array, Dqn_usize count); -DQN_API template bool Dqn_FArray_IsValid (Dqn_FArray const *array); +DQN_API template Dqn_FArray Dqn_FArray_Init (T const *array, Dqn_usize count); +DQN_API template bool Dqn_FArray_IsValid (Dqn_FArray const *array); +DQN_API template Dqn_usize Dqn_FArray_Max (Dqn_FArray const *) { return N; } -// NOTE: Insert ==================================================================================== -DQN_API template T * Dqn_FArray_Make (Dqn_FArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); -DQN_API template T * Dqn_FArray_Add (Dqn_FArray *array, T const *items, Dqn_usize count); - -// NOTE: Modify ==================================================================================== -DQN_API template void Dqn_FArray_EraseRange(Dqn_FArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); -DQN_API template void Dqn_FArray_Clear (Dqn_FArray *array); +// NOTE: API ======================================================================================= +DQN_API template T * Dqn_FArray_MakeArray (Dqn_FArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_FArray_Make (Dqn_FArray *array, Dqn_ZeroMem zero_mem); +DQN_API template T * Dqn_FArray_AddArray (Dqn_FArray *array, T const *items, Dqn_usize count); +DQN_API template T * Dqn_FArray_Add (Dqn_FArray *array, T const &item); +DQN_API template T * Dqn_FArray_InsertArray(Dqn_FArray *array, T const &item, Dqn_usize index); +DQN_API template T * Dqn_FArray_Insert (Dqn_FArray *array, Dqn_usize index, T const &item); +DQN_API template T Dqn_FArray_PopFront (Dqn_FArray *array, Dqn_usize count); +DQN_API template T Dqn_FArray_PopBack (Dqn_FArray *array, Dqn_usize count); +DQN_API template Dqn_ArrayFindResult Dqn_FArray_Find (Dqn_FArray *array, T const &find); +DQN_API template Dqn_ArrayEraseResult Dqn_FArray_EraseRange (Dqn_FArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase); +DQN_API template void Dqn_FArray_Clear (Dqn_FArray *array); #endif // !defined(DQN_NO_FARRAY) #if !defined(DQN_NO_SLICE) // NOTE: [$SLIC] Dqn_Slice ========================================================================= -// A pointer and length container of data +template Dqn_Slice Dqn_Slice_Init (T* const data, Dqn_usize size); +template Dqn_Slice Dqn_Slice_InitCArrayCopy(Dqn_Arena *arena, T const *(&array)[N]); +template Dqn_Slice Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); -template struct Dqn_Slice -{ - T *data; - Dqn_usize size; - - T *begin() { return data; } - T *end () { return data + size; } - T const *begin() const { return data; } - T const *end () const { return data + size; } -}; - -template Dqn_Slice Dqn_Slice_Init (T* const data, Dqn_usize size); -template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); #endif // !defined(DQN_NO_SLICE) #if !defined(DQN_NO_DSMAP) @@ -241,30 +268,27 @@ template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize s // @proc Dqn_DSMap_HashToSlotIndex // @desc Calculate the index into the map's `slots` array from the given hash. -// @proc Dqn_DSMap_FindSlot, Dqn_DSMap_Find +// @proc Dqn_DSMap_Find // @desc Find the slot in the map's `slots` array corresponding to the given -// key and hash. If the map does not contain the key, a null pointer is -// returned. +// key and hash. If the map does not contain the key `found` is set to false +// and `slot` and `value` are null. // // `Find` returns the value. // `FindSlot` returns the map's hash table slot. -// @proc Dqn_DSMap_MakeSlot, Dqn_DSMap_Make, Dqn_DSMap_Set, Dqn_DSMap_SetSlot +// @proc Dqn_DSMap_Make, Dqn_DSMap_Set // @desc Same as `DSMap_Find*` except if the key does not exist in the table, // a hash-table slot will be made. // -// `Make` assigns the key to the table and returns the hash table slot's value. -// `Set` assigns the key-value to the table and returns the hash table slot's value. -// `MakeSlot` assigns the key to the table and returns the hash table slot. -// `SetSlot` assigns the key-value to the table and returns the hash table slot. +// `Make` assigns the key to the table and returns the hash table slot's value. +// `Set` assigns the key-value to the table and returns the hash table slot's value. // // If by adding the key-value pair to the table puts the table over 75% load, // the table will be grown to 2x the current the size before insertion // completes. // -// @param found[out] Pass a pointer to a bool. The bool will be set to true -// if the item already existed in the map before, or false if the item was -// just created by this call. +// `found` will be set to true if the item already existed in the map before, +// or false if the item was just created by this call. // @proc Dqn_DSMap_Resize // @desc Resize the table and move all elements to the new map. @@ -280,14 +304,14 @@ template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize s // initial size that the table was initialised as. // @proc Dqn_DSMap_KeyCStringLit, Dqn_DSMap_KeyU64, Dqn_DSMap_KeyBuffer, -// Dqn_DSMap_KeyString8 Dqn_DSMap_KeyString8Copy +// Dqn_DSMap_KeyStr8 Dqn_DSMap_KeyStr8Copy // @desc Create a hash-table key given // // `KeyCStringLit` a cstring literal // `KeyU64` a u64 // `KeyBuffer` a (ptr+len) slice of bytes -// `KeyString8` a Dqn_String8 string -// `KeyString8Copy` a Dqn_String8 string that is copied first using the allocator +// `KeyStr8` a Dqn_Str8 string +// `KeyStr8Copy` a Dqn_Str8 string that is copied first using the allocator // // If the key points to an array of bytes, the lifetime of those bytes *must* // remain valid throughout the lifetime of the map as the pointers are value @@ -343,33 +367,50 @@ template struct Dqn_DSMap }; // NOTE: Setup ===================================================================================== -DQN_API template Dqn_DSMap Dqn_DSMap_Init (uint32_t size); -DQN_API template void Dqn_DSMap_Deinit (Dqn_DSMap *map); -DQN_API template bool Dqn_DSMap_IsValid (Dqn_DSMap const *map); +DQN_API template Dqn_DSMap Dqn_DSMap_Init (uint32_t size); +DQN_API template void Dqn_DSMap_Deinit (Dqn_DSMap *map); +DQN_API template bool Dqn_DSMap_IsValid (Dqn_DSMap const *map); // NOTE: Hash ====================================================================================== -DQN_API template uint32_t Dqn_DSMap_Hash (Dqn_DSMap const *map, Dqn_DSMapKey key); -DQN_API template uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap const *map, Dqn_DSMapKey key); +DQN_API template uint32_t Dqn_DSMap_Hash (Dqn_DSMap const *map, Dqn_DSMapKey key); +DQN_API template uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap const *map, Dqn_DSMapKey key); // NOTE: Insert ==================================================================================== -DQN_API template Dqn_DSMapSlot *Dqn_DSMap_FindSlot (Dqn_DSMap const *map, Dqn_DSMapKey key); -DQN_API template Dqn_DSMapSlot *Dqn_DSMap_MakeSlot (Dqn_DSMap *map, Dqn_DSMapKey key, bool *found); -DQN_API template Dqn_DSMapSlot *Dqn_DSMap_SetSlot (Dqn_DSMap *map, Dqn_DSMapKey key, T const &value, bool *found); -DQN_API template T * Dqn_DSMap_Find (Dqn_DSMap const *map, Dqn_DSMapKey key); -DQN_API template T * Dqn_DSMap_Make (Dqn_DSMap *map, Dqn_DSMapKey key, bool *found); -DQN_API template T * Dqn_DSMap_Set (Dqn_DSMap *map, Dqn_DSMapKey key, T const &value, bool *found); -DQN_API template bool Dqn_DSMap_Resize (Dqn_DSMap *map, uint32_t size); -DQN_API template bool Dqn_DSMap_Erase (Dqn_DSMap *map, Dqn_DSMapKey key); +template +struct Dqn_DSMapResult +{ + bool found; + Dqn_DSMapSlot *slot; + T *value; +}; + +DQN_API template Dqn_DSMapResult Dqn_DSMap_Find (Dqn_DSMap const *map, Dqn_DSMapKey key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_Make (Dqn_DSMap *map, Dqn_DSMapKey key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_Set (Dqn_DSMap *map, Dqn_DSMapKey key, T const &value); + +DQN_API template Dqn_DSMapResult Dqn_DSMap_FindKeyU64 (Dqn_DSMap const *map, uint64_t key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_MakeKeyU64 (Dqn_DSMap *map, uint64_t key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_SetKeyU64 (Dqn_DSMap *map, uint64_t key, T const &value); + +DQN_API template Dqn_DSMapResult Dqn_DSMap_FindKeyStr8 (Dqn_DSMap const *map, Dqn_Str8 key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8 (Dqn_DSMap *map, Dqn_Str8 key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_SetKeyStr8 (Dqn_DSMap *map, Dqn_Str8 key, T const &value); + +DQN_API template Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8Copy(Dqn_DSMap *map, Dqn_Allocator allocator, Dqn_Str8 key); +DQN_API template Dqn_DSMapResult Dqn_DSMap_SetKeyStr8Copy (Dqn_DSMap *map, Dqn_Allocator allocator, Dqn_Str8 key, T const &value); + +DQN_API template bool Dqn_DSMap_Resize (Dqn_DSMap *map, uint32_t size); +DQN_API template bool Dqn_DSMap_Erase (Dqn_DSMap *map, Dqn_DSMapKey key); // NOTE: Table Keys ================================================================================ -DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap const *map, void const *data, uint32_t size); -DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap const *map, uint64_t u64); -DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyString8 (Dqn_DSMap const *map, Dqn_String8 string); -DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyString8Copy (Dqn_DSMap const *map, Dqn_Allocator allocator, Dqn_String8 string); -#define Dqn_DSMap_KeyCStringLit(map, string) Dqn_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1) -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash (uint64_t u64); -DQN_API bool Dqn_DSMap_KeyEquals (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); -DQN_API bool operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); +DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap const *map, void const *data, uint32_t size); +DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap const *map, uint64_t u64); +DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyStr8 (Dqn_DSMap const *map, Dqn_Str8 string); +DQN_API template Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy (Dqn_DSMap const *map, Dqn_Allocator allocator, Dqn_Str8 string); +#define Dqn_DSMap_KeyCStr8(map, string) Dqn_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1) +DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash (uint64_t u64); +DQN_API bool Dqn_DSMap_KeyEquals (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); +DQN_API bool operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs); #endif // !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_LIST) @@ -408,6 +449,7 @@ template struct Dqn_ListIterator Dqn_b32 init; // True if Dqn_List_Iterate has been called at-least once on this iterator Dqn_ListChunk *chunk; // The chunk that the iterator is reading from Dqn_usize chunk_data_index; // The index in the chunk the iterator is referencing + Dqn_usize index; // The index of the item in the list as if it was flat array T *data; // Pointer to the data the iterator is referencing. Nullptr if invalid. }; @@ -421,18 +463,21 @@ template struct Dqn_List }; // NOTE: API ======================================================================================= -template Dqn_List Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size); -template T * Dqn_List_At (Dqn_List *list, Dqn_usize index, Dqn_ListChunk *at_chunk); -template bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index); +template Dqn_List Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size); +template Dqn_List Dqn_List_InitCArrayCopy (Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]); +template T * Dqn_List_At (Dqn_List *list, Dqn_usize index, Dqn_ListChunk *at_chunk); +template bool Dqn_List_Iterate (Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index); -template T * Dqn_List_Make (Dqn_List *list, Dqn_usize count); -template T * Dqn_List_Add (Dqn_List *list, T const &value); +template T * Dqn_List_Make (Dqn_List *list, Dqn_usize count); +template T * Dqn_List_Add (Dqn_List *list, T const &value); +template void Dqn_List_AddList (Dqn_List *list, Dqn_List other); +template Dqn_Slice Dqn_List_ToSliceCopy(Dqn_List const *list, Dqn_Arena* arena); #endif // !defined(DQN_NO_LIST) // NOTE: [$CARR] Dqn_CArray ======================================================================== -template Dqn_usize Dqn_CArray_EraseRange(T* data, Dqn_usize *size, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +template Dqn_ArrayEraseResult Dqn_CArray_EraseRange(T* data, Dqn_usize *size, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) { - Dqn_usize result = 0; + Dqn_ArrayEraseResult result = {}; if (!data || !size || *size == 0 || count == 0) return result; @@ -476,16 +521,17 @@ template Dqn_usize Dqn_CArray_EraseRange(T* data, Dqn_usize *size, *size -= erase_count; } - result = erase_count; + result.items_erased = erase_count; + result.it_index = begin_index; return result; } -template T *Dqn_CArray_Make(T* data, Dqn_usize *size, Dqn_usize max, Dqn_usize count, Dqn_ZeroMem zero_mem) +template T *Dqn_CArray_MakeArray(T* data, Dqn_usize *size, Dqn_usize max, Dqn_usize count, Dqn_ZeroMem zero_mem) { if (!data || !size || count == 0) return nullptr; - if (!DQN_CHECKF((*size + count) < max, "Array is out of memory")) + if (!DQN_CHECKF((*size + count) <= max, "Array is out of memory")) return nullptr; // TODO: Use placement new? Why doesn't this work? @@ -496,6 +542,69 @@ template T *Dqn_CArray_Make(T* data, Dqn_usize *size, Dqn_usize max return result; } +DQN_API template T *Dqn_CArray_InsertArray(T *data, Dqn_usize *size, Dqn_usize max, Dqn_usize index, T const *items, Dqn_usize count) +{ + T *result = nullptr; + if (!data || !size || !items || count <= 0 || ((*size + count) > max)) + return result; + + Dqn_usize clamped_index = *size ? DQN_MIN(index, *size) : 0; + if (clamped_index != *size) { + char const *src = DQN_CAST(char *)(data + clamped_index); + char const *dest = DQN_CAST(char *)(data + (clamped_index + count)); + char const *end = DQN_CAST(char *)(data + (*size)); + Dqn_usize bytes_to_move = end - src; + DQN_MEMMOVE(DQN_CAST(void *)dest, src, bytes_to_move); + } + + result = data + clamped_index; + DQN_MEMCPY(result, items, sizeof(T) * count); + *size += count; + return result; +} + +template T Dqn_CArray_PopFront(T* data, Dqn_usize *size, Dqn_usize count) +{ + T result = {}; + if (!data || !size || *size <= 0) + return result; + + result = data[0]; + Dqn_usize pop_count = DQN_MIN(count, *size); + DQN_MEMMOVE(data, data + pop_count, (*size - pop_count) * sizeof(T)); + *size -= pop_count; + return result; +} + +template T Dqn_CArray_PopBack(T* data, Dqn_usize *size, Dqn_usize count) +{ + T result = {}; + if (!data || !size || *size <= 0) + return result; + + Dqn_usize pop_count = DQN_MIN(count, *size); + result = data[(*size - 1)]; + *size -= pop_count; + return result; +} + +template Dqn_ArrayFindResult Dqn_CArray_Find(T *data, Dqn_usize size, T const &value) +{ + Dqn_ArrayFindResult result = {}; + if (!data || size <= 0) + return result; + + for (Dqn_usize index = 0; !result.data && index < size; index++) { + T *item = data + index; + if (*item == value) { + result.data = item; + result.index = index; + } + } + + return result; +} + #if !defined(DQN_NO_VARRAY) // NOTE: [$VARR] Dqn_VArray ======================================================================== DQN_API template Dqn_VArray Dqn_VArray_InitByteSize(Dqn_Arena *arena, Dqn_usize byte_size) @@ -520,7 +629,7 @@ DQN_API template bool Dqn_VArray_IsValid(Dqn_VArray const *array return result; } -DQN_API template T *Dqn_VArray_Make(Dqn_VArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) +DQN_API template T *Dqn_VArray_MakeArray(Dqn_VArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) { if (!Dqn_VArray_IsValid(array)) return nullptr; @@ -535,26 +644,72 @@ DQN_API template T *Dqn_VArray_Make(Dqn_VArray *array, Dqn_usize return result; } -DQN_API template T *Dqn_VArray_Add(Dqn_VArray *array, T const *items, Dqn_usize count) +DQN_API template T *Dqn_VArray_Make(Dqn_VArray *array, Dqn_ZeroMem zero_mem) { - T *result = Dqn_VArray_Make(array, count, Dqn_ZeroMem_No); + T *result = Dqn_VArray_MakeArray(array, 1, zero_mem); + return result; +} + +DQN_API template T *Dqn_VArray_AddArray(Dqn_VArray *array, T const *items, Dqn_usize count) +{ + T *result = Dqn_VArray_MakeArray(array, count, Dqn_ZeroMem_No); if (result) DQN_MEMCPY(result, items, count * sizeof(T)); return result; } -DQN_API template void Dqn_VArray_EraseRange(Dqn_VArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +DQN_API template T *Dqn_VArray_Add(Dqn_VArray *array, T const &item) { - if (!Dqn_VArray_IsValid(array)) - return; - Dqn_usize erase_count = Dqn_CArray_EraseRange(array->data, &array->size, begin_index, count, erase); - Dqn_MemBlock_Pop(array->block, erase_count * sizeof(T)); + T *result = Dqn_VArray_AddArray(array, &item, 1); + return result; } -DQN_API template void Dqn_VArray_Clear(Dqn_VArray *array) +DQN_API template T *Dqn_VArray_InsertArray(Dqn_VArray *array, Dqn_usize index, T const *items, Dqn_usize count) { - if (array) + T *result = nullptr; + if (!Dqn_VArray_IsValid(array)) + return result; + Dqn_VArray_Reserve(array, array->size + count); + result = Dqn_CArray_InsertArray(array->data, &array->size, array->max, index, items, count); + return result; +} + +DQN_API template T *Dqn_VArray_Insert(Dqn_VArray *array, Dqn_usize index, T const &item) +{ + T *result = Dqn_VArray_InsertArray(array, index, &item, 1); + return result; +} + +DQN_API template T *Dqn_VArray_PopFront(Dqn_VArray *array, Dqn_usize count) +{ + T *result = Dqn_CArray_PopFront(array->data, &array->size, count); + return result; +} + +DQN_API template T *Dqn_VArray_PopBack(Dqn_VArray *array, Dqn_usize count) +{ + T *result = Dqn_CArray_PopBack(array->data, &array->size, count); + return result; +} + +DQN_API template Dqn_ArrayEraseResult Dqn_VArray_EraseRange(Dqn_VArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +{ + Dqn_ArrayEraseResult result = {}; + if (!Dqn_VArray_IsValid(array)) + return result; + result = Dqn_CArray_EraseRange(array->data, &array->size, begin_index, count, erase); + Dqn_MemBlock_Pop(array->block, result.items_erased * sizeof(T)); + return result; +} + +DQN_API template void Dqn_VArray_Clear(Dqn_VArray *array, Dqn_ZeroMem zero_mem) +{ + if (array) { + if (zero_mem == Dqn_ZeroMem_Yes) + DQN_MEMSET(array->data, 0, array->size * sizeof(T)); + Dqn_MemBlock_PopTo(array->block, 0); array->size = 0; + } } DQN_API template void Dqn_VArray_Reserve(Dqn_VArray *array, Dqn_usize count) @@ -571,25 +726,45 @@ DQN_API template void Dqn_VArray_Reserve(Dqn_VArray *array, Dqn_ DQN_API template Dqn_SArray Dqn_SArray_Init(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) { Dqn_SArray result = {}; - if (!arena || !count) + if (!arena || !size) return result; - result.data = Dqn_Arena_NewArray(arena, T, size, zero_mem); if (result.data) result.max = size; return result; } + +template Dqn_SArray Dqn_SArray_InitCArrayCopy(Dqn_Arena *arena, T const (&array)[N]) +{ + Dqn_SArray result = {}; + if (!arena || !N) + return result; + result.data = Dqn_Arena_NewArray(arena, T, N, Dqn_ZeroMem_No); + if (result.data) { + DQN_MEMCOPY(result.data, array, sizeof(T) * N); + result.size = size; + result.max = size; + } + return result; +} + DQN_API template bool Dqn_SArray_IsValid(Dqn_SArray const *array) { bool result = array && array->data && array->size <= array->max; return result; } -DQN_API template T *Dqn_SArray_Make(Dqn_SArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) +DQN_API template T *Dqn_SArray_MakeArray(Dqn_SArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) { if (!Dqn_SArray_IsValid(array)) return nullptr; - T *result = Dqn_CArray_Make(array->data, &array->size, array->max, count, zero_mem); + T *result = Dqn_CArray_MakeArray(array->data, &array->size, array->max, count, zero_mem); + return result; +} + +DQN_API template T *Dqn_SArray_Make(Dqn_SArray *array, Dqn_ZeroMem zero_mem) +{ + T *result = Dqn_SArray_MakeArray(array, 1, zero_mem); return result; } @@ -606,11 +781,38 @@ DQN_API template T *Dqn_SArray_Add(Dqn_SArray *array, T const &i return result; } -DQN_API template void Dqn_SArray_EraseRange(Dqn_SArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +DQN_API template T *Dqn_SArray_InsertArray(Dqn_SArray *array, Dqn_usize index, T const *items, Dqn_usize count) +{ + if (!Dqn_SArray_IsValid(array)) + return result; + T *result = Dqn_CArray_InsertArray(array->data, &array->size, array->max, index, items, count); + return result; +} + +DQN_API template T *Dqn_SArray_Insert(Dqn_SArray *array, Dqn_usize index, T const &item) +{ + T *result = Dqn_SArray_InsertArray(array, index, &item, 1); + return result; +} + +DQN_API template T Dqn_SArray_PopFront(Dqn_SArray *array, Dqn_usize count) +{ + T result = Dqn_CArray_PopFront(array->data, &array->size, count); + return result; +} + +DQN_API template T Dqn_SArray_PopBack(Dqn_SArray *array, Dqn_usize count) +{ + T result = Dqn_CArray_PopBack(array->data, &array->size, count); + return result; +} + +DQN_API template Dqn_ArrayEraseResult Dqn_SArray_EraseRange(Dqn_SArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) { if (!Dqn_SArray_IsValid(array) || array->size == 0 || count == 0) return; - Dqn_CArray_EraseRange(array->data, &array->size, being_index, count, erase); + Dqn_ArrayEraseResult result = Dqn_CArray_EraseRange(array->data, &array->size, being_index, count, erase); + return result; } DQN_API template void Dqn_SArray_Clear(Dqn_SArray *array) @@ -625,7 +827,7 @@ DQN_API template void Dqn_SArray_Clear(Dqn_SArray *array) DQN_API template Dqn_FArray Dqn_FArray_Init(T const *array, Dqn_usize count) { Dqn_FArray result = {}; - bool added = Dqn_FArray_Add(&result, array, count); + bool added = Dqn_FArray_AddArray(&result, array, count); DQN_ASSERT(added); return result; } @@ -635,27 +837,74 @@ DQN_API template bool Dqn_FArray_IsValid(Dqn_FArray T *Dqn_FArray_Make(Dqn_FArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) +DQN_API template T *Dqn_FArray_MakeArray(Dqn_FArray *array, Dqn_usize count, Dqn_ZeroMem zero_mem) { if (!Dqn_FArray_IsValid(array)) return nullptr; - T *result = Dqn_CArray_Make(array->data, &array->size, N, count, zero_mem); + T *result = Dqn_CArray_MakeArray(array->data, &array->size, N, count, zero_mem); return result; } -DQN_API template T *Dqn_FArray_Add(Dqn_FArray *array, T const *items, Dqn_usize count) +DQN_API template T *Dqn_FArray_Make(Dqn_FArray *array, Dqn_ZeroMem zero_mem) { - T *result = Dqn_FArray_Make(array, count, Dqn_ZeroMem_No); + T *result = Dqn_FArray_MakeArray(array, 1, zero_mem); + return result; +} + +DQN_API template T *Dqn_FArray_AddArray(Dqn_FArray *array, T const *items, Dqn_usize count) +{ + T *result = Dqn_FArray_MakeArray(array, count, Dqn_ZeroMem_No); if (result) DQN_MEMCPY(result, items, count * sizeof(T)); return result; } -DQN_API template void Dqn_FArray_EraseRange(Dqn_FArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +DQN_API template T *Dqn_FArray_Add(Dqn_FArray *array, T const &item) { + T *result = Dqn_FArray_AddArray(array, &item, 1); + return result; +} + +DQN_API template T *Dqn_FArray_InsertArray(Dqn_FArray *array, Dqn_usize index, T const *items, Dqn_usize count) +{ + T *result = nullptr; + if (!Dqn_FArray_IsValid(array)) + return result; + result = Dqn_CArray_InsertArray(array->data, &array->size, N, index, items, count); + return result; +} + +DQN_API template T *Dqn_FArray_Insert(Dqn_FArray *array, Dqn_usize index, T const &item) +{ + T *result = Dqn_FArray_InsertArray(array, index, &item, 1); + return result; +} + +DQN_API template T Dqn_FArray_PopFront(Dqn_FArray *array, Dqn_usize count) +{ + T result = Dqn_CArray_PopFront(array->data, &array->size, count); + return result; +} + +DQN_API template T Dqn_FArray_PopBack(Dqn_FArray *array, Dqn_usize count) +{ + T result = Dqn_CArray_PopBack(array->data, &array->size, count); + return result; +} + +DQN_API template Dqn_ArrayFindResult Dqn_FArray_Find(Dqn_FArray *array, T const &find) +{ + Dqn_ArrayFindResult result = Dqn_CArray_Find(array->data, array->size, find); + return result; +} + +DQN_API template Dqn_ArrayEraseResult Dqn_FArray_EraseRange(Dqn_FArray *array, Dqn_usize begin_index, Dqn_isize count, Dqn_ArrayErase erase) +{ + Dqn_ArrayEraseResult result = {}; if (!Dqn_FArray_IsValid(array) || array->size == 0 || count == 0) - return; - Dqn_CArray_EraseRange(array->data, &array->size, begin_index, count, erase); + return result; + result = Dqn_CArray_EraseRange(array->data, &array->size, begin_index, count, erase); + return result; } DQN_API template void Dqn_FArray_Clear(Dqn_FArray *array) @@ -676,6 +925,15 @@ template Dqn_Slice Dqn_Slice_Init(T* const data, Dqn_usize size) return result; } +template +Dqn_Slice Dqn_Slice_InitCArrayCopy(Dqn_Arena *arena, T const (&array)[N]) +{ + Dqn_Slice result = Dqn_Slice_Alloc(arena, N, Dqn_ZeroMem_No); + if (result.data) + DQN_MEMCPY(result.data, array, sizeof(T) * N); + return result; +} + template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) { Dqn_Slice result = {}; @@ -686,6 +944,7 @@ template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize s result.size = size; return result; } + #endif // !defined(DQN_NO_SLICE) #if !defined(DQN_NO_DSMAP) @@ -830,83 +1089,125 @@ uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap const *map, Dqn_DSMapKey key) } template -Dqn_DSMapSlot *Dqn_DSMap_FindSlot(Dqn_DSMap const *map, Dqn_DSMapKey key) +Dqn_DSMapResult Dqn_DSMap_Find(Dqn_DSMap const *map, Dqn_DSMapKey key) { - Dqn_DSMapSlot const *result = nullptr; + Dqn_DSMapResult result = {}; if (Dqn_DSMap_IsValid(map)) { uint32_t index = Dqn_DSMap_HashToSlotIndex(map, key); - if (map->hash_to_slot[index] != DQN_DS_MAP_SENTINEL_SLOT) - result = map->slots + map->hash_to_slot[index]; - } - return DQN_CAST(Dqn_DSMapSlot *)result; -} - -template -Dqn_DSMapSlot *Dqn_DSMap_MakeSlot(Dqn_DSMap *map, Dqn_DSMapKey key, bool *found) -{ - Dqn_DSMapSlot *result = nullptr; - if (Dqn_DSMap_IsValid(map)) { - uint32_t index = Dqn_DSMap_HashToSlotIndex(map, key); - if (map->hash_to_slot[index] == DQN_DS_MAP_SENTINEL_SLOT) { - // NOTE: Create the slot - map->hash_to_slot[index] = map->occupied++; - - // NOTE: Check if resize is required - bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3); - if (map_is_75pct_full) { - if (!Dqn_DSMap_Resize(map, map->size * 2)) - return result; - result = Dqn_DSMap_MakeSlot(map, key, nullptr /*found*/); - } else { - result = map->slots + map->hash_to_slot[index]; - } - - // NOTE: Update the slot - result->key = key; - if (found) - *found = false; - } else { - result = map->slots + map->hash_to_slot[index]; - if (found) - *found = true; + if (map->hash_to_slot[index] != DQN_DS_MAP_SENTINEL_SLOT) { + result.slot = map->slots + map->hash_to_slot[index]; + result.value = &result.slot->value; + result.found = true; } } return result; } template -Dqn_DSMapSlot *Dqn_DSMap_SetSlot(Dqn_DSMap *map, Dqn_DSMapKey key, T const &value, bool *found) +Dqn_DSMapResult Dqn_DSMap_Make(Dqn_DSMap *map, Dqn_DSMapKey key) { - Dqn_DSMapSlot *result = nullptr; + Dqn_DSMapResult result = {}; if (!Dqn_DSMap_IsValid(map)) return result; - result = Dqn_DSMap_MakeSlot(map, key, found); - result->value = value; + uint32_t index = Dqn_DSMap_HashToSlotIndex(map, key); + if (map->hash_to_slot[index] == DQN_DS_MAP_SENTINEL_SLOT) { + // NOTE: Create the slot + map->hash_to_slot[index] = map->occupied++; + + // NOTE: Check if resize is required + bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3); + if (map_is_75pct_full) { + if (!Dqn_DSMap_Resize(map, map->size * 2)) + return result; + result = Dqn_DSMap_Make(map, key); + } else { + result.slot = map->slots + map->hash_to_slot[index]; + result.slot->key = key; // NOTE: Assign key to new slot + } + } else { + result.slot = map->slots + map->hash_to_slot[index]; + result.found = true; + } + + result.value = &result.slot->value; return result; } template -T *Dqn_DSMap_Find(Dqn_DSMap const *map, Dqn_DSMapKey key) +Dqn_DSMapResult Dqn_DSMap_Set(Dqn_DSMap *map, Dqn_DSMapKey key, T const &value) { - Dqn_DSMapSlot const *slot = Dqn_DSMap_FindSlot(map, key); - T const *result = slot ? &slot->value : nullptr; - return DQN_CAST(T *)result; -} + Dqn_DSMapResult result = {}; + if (!Dqn_DSMap_IsValid(map)) + return result; -template -T *Dqn_DSMap_Make(Dqn_DSMap *map, Dqn_DSMapKey key, bool *found) -{ - Dqn_DSMapSlot *slot = Dqn_DSMap_MakeSlot(map, key, found); - T *result = &slot->value; + result = Dqn_DSMap_Make(map, key); + result.slot->value = value; return result; } template -T *Dqn_DSMap_Set(Dqn_DSMap *map, Dqn_DSMapKey key, T const &value, bool *found) +Dqn_DSMapResult Dqn_DSMap_FindKeyU64(Dqn_DSMap const *map, uint64_t key) { - Dqn_DSMapSlot *result = Dqn_DSMap_SetSlot(map, key, value, found); - return &result->value; + Dqn_DSMapKey map_key = Dqn_DSMap_KeyU64(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Find(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_MakeKeyU64(Dqn_DSMap *map, uint64_t key) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyU64(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Make(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_SetKeyU64(Dqn_DSMap *map, uint64_t key, T const &value) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyU64(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Set(map, map_key, value); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_FindKeyStr8(Dqn_DSMap const *map, Dqn_Str8 key) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Find(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8(Dqn_DSMap *map, Dqn_Str8 key) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Make(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_SetKeyStr8(Dqn_DSMap *map, Dqn_Str8 key, T const &value) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8(map, key); + Dqn_DSMapResult result = Dqn_DSMap_Set(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_MakeKeyStr8Copy(Dqn_DSMap *map, Dqn_Allocator allocator, Dqn_Str8 key) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, allocator, key); + Dqn_DSMapResult result = Dqn_DSMap_Make(map, map_key); + return result; +} + +template +Dqn_DSMapResult Dqn_DSMap_SetKeyStr8Copy(Dqn_DSMap *map, Dqn_Allocator allocator, Dqn_Str8 key, T const &value) +{ + Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, allocator, key); + Dqn_DSMapResult result = Dqn_DSMap_Set(map, map_key); + return result; } template @@ -923,7 +1224,7 @@ bool Dqn_DSMap_Resize(Dqn_DSMap *map, uint32_t size) for (uint32_t old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) { Dqn_DSMapSlot *old_slot = map->slots + old_index; if (old_slot->key.type != Dqn_DSMapKeyType_Invalid) { - Dqn_DSMap_Set(&new_map, old_slot->key, old_slot->value, nullptr /*found*/); + Dqn_DSMap_Set(&new_map, old_slot->key, old_slot->value); } } @@ -933,7 +1234,6 @@ bool Dqn_DSMap_Resize(Dqn_DSMap *map, uint32_t size) return true; } - template bool Dqn_DSMap_Erase(Dqn_DSMap *map, Dqn_DSMapKey key) { @@ -970,7 +1270,6 @@ bool Dqn_DSMap_Erase(Dqn_DSMap *map, Dqn_DSMapKey key) map->hash_to_slot[index] = map->hash_to_slot[probe_index]; map->hash_to_slot[probe_index] = DQN_DS_MAP_SENTINEL_SLOT; index = probe_index; - DQN_ASSERT(Dqn_DSMap_FindSlot(map, probe->key) == probe); } // NOTE: We have erased a slot from the hash table, this leaves a gap @@ -1020,7 +1319,7 @@ DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap const *map, uint64_t u64) } template -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyString8(Dqn_DSMap const *map, Dqn_String8 string) +DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap const *map, Dqn_Str8 string) { DQN_ASSERT(string.size > 0 && string.size <= UINT32_MAX); Dqn_DSMapKey result = {}; @@ -1032,10 +1331,10 @@ DQN_API Dqn_DSMapKey Dqn_DSMap_KeyString8(Dqn_DSMap const *map, Dqn_String8 s } template -DQN_API Dqn_DSMapKey Dqn_DSMap_KeyString8Copy(Dqn_DSMap const *map, Dqn_Allocator allocator, Dqn_String8 string) +DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy(Dqn_DSMap const *map, Dqn_Allocator allocator, Dqn_Str8 string) { - Dqn_String8 copy = Dqn_String8_Copy(allocator, string); - Dqn_DSMapKey result = Dqn_DSMap_KeyString8(map, copy); + Dqn_Str8 copy = Dqn_Str8_Copy(allocator, string); + Dqn_DSMapKey result = Dqn_DSMap_KeyStr8(map, copy); return result; } #endif // !defined(DQN_NO_DSMAP) @@ -1050,6 +1349,22 @@ template DQN_API Dqn_List Dqn_List_Init(Dqn_Arena *arena, Dqn_us return result; } +template Dqn_List Dqn_List_InitCArrayCopy(Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]) +{ + Dqn_List result = Dqn_List_Init(arena, chunk_size); + DQN_FOR_UINDEX (index, N) + Dqn_List_Add(&result, array[index]); + return result; +} + +template Dqn_List Dqn_List_InitSliceCopy(Dqn_Arena *arena, Dqn_usize chunk_size, Dqn_Slice slice) +{ + Dqn_List result = Dqn_List_Init(arena, chunk_size); + DQN_FOR_UINDEX (index, slice.size) + Dqn_List_Add(&result, slice.data[index]); + return result; +} + template DQN_API T *Dqn_List_Make(Dqn_List *list, Dqn_usize count) { if (list->chunk_size == 0) @@ -1090,13 +1405,25 @@ template DQN_API T *Dqn_List_Add(Dqn_List *list, T const &value) return result; } +template DQN_API void Dqn_List_AddList(Dqn_List *list, Dqn_List other) +{ + if (!list || list->chunk_size <= 0) + return; + + // TODO(doyle): Copy chunk by chunk + for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); ) + Dqn_List_Add(list, *it.data); +} + template DQN_API bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_usize start_index) { bool result = false; if (!list || !it || list->chunk_size <= 0) return result; - if (!it->init) { + if (it->init) { + it->index++; + } else { *it = {}; if (start_index == 0) { it->chunk = list->head; @@ -1161,4 +1488,17 @@ template DQN_API T *Dqn_List_At(Dqn_List *list, Dqn_usize index, return result; } + +template Dqn_Slice Dqn_List_ToSliceCopy(Dqn_List const *list, Dqn_Arena *arena) +{ + // TODO(doyle): Chunk memcopies is much faster + Dqn_Slice result = Dqn_Slice_Alloc(arena, list->count, Dqn_ZeroMem_No); + if (result.size) { + Dqn_usize slice_index = 0; + for (Dqn_ListIterator it = {}; Dqn_List_Iterate(DQN_CAST(Dqn_List *)list, &it, 0);) + result.data[slice_index++] = *it.data; + DQN_ASSERT(slice_index == result.size); + } + return result; +} #endif // !defined(DQN_NO_LIST) diff --git a/dqn_cppbuild.h b/dqn_cppbuild.h new file mode 100644 index 0000000..fd3f27c --- /dev/null +++ b/dqn_cppbuild.h @@ -0,0 +1,183 @@ +#if !defined(DQN_CPP_BUILD_H) +#define DQN_CPP_BUILD_H + +struct Dqn_CPPBuildCompileFile +{ + Dqn_Slice flags; + Dqn_Str8 input_file_path; + Dqn_Str8 output_file_path; +}; + +Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_OBJ = DQN_STR8(".obj"); +Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_O = DQN_STR8(".o"); + +enum Dqn_CPPBuildCompiler +{ + Dqn_CPPBuildCompiler_MSVC, + Dqn_CPPBuildCompiler_GCC, +}; + +struct Dqn_CPPBuildContext +{ + Dqn_CPPBuildCompiler compiler; + Dqn_Str8 compile_file_obj_suffix; + Dqn_Slice compile_files; + Dqn_Slice compile_flags; + Dqn_Slice include_dirs; + Dqn_Slice link_flags; + Dqn_Str8 build_dir; +}; + +enum Dqn_CPPBuildStatus +{ + Dqn_CPPBuildStatus_Ok, + Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade, +}; + +struct Dqn_CPPBuildAsyncResult +{ + Dqn_CPPBuildStatus status; + Dqn_OSExecAsyncHandle async_handle; +}; + +enum Dqn_CPPBuildMode +{ + Dqn_CPPBuildMode_AlwaysRebuild, + Dqn_CPPBuildMode_CacheBuild, +}; + +DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator); +DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode); +DQN_API void Dqn_CPPBuild_ExecOrAbort (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode); +#endif // DQN_CPP_BUILD_H + +#if defined(DQN_CPP_BUILD_IMPLEMENTATION) +DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator) +{ + // NOTE: Check if object files are newer than the source files ================================= + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); + Dqn_Str8 result = {}; + + Dqn_Slice dirtied_compile_files = build_context.compile_files; + if (mode == Dqn_CPPBuildMode_CacheBuild) { + dirtied_compile_files = Dqn_Slice_Alloc(scratch.arena, build_context.compile_files.size, Dqn_ZeroMem_Yes); + dirtied_compile_files.size = 0; + + DQN_FOR_UINDEX (index, build_context.compile_files.size) { + Dqn_CPPBuildCompileFile file = build_context.compile_files.data[index]; + + Dqn_Str8 obj_file_name = {}; + if (file.output_file_path.size) { + obj_file_name = file.output_file_path; + } else { + // NOTE: Determine the object file suffix + Dqn_Str8 compile_file_obj_suffix = build_context.compile_file_obj_suffix; + if (compile_file_obj_suffix.size == 0) + compile_file_obj_suffix = DQN_CPP_BUILD_OBJ_SUFFIX_OBJ; + + // NOTE: Create the object file path + Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(file.input_file_path); + obj_file_name = Dqn_Str8_InitF(scratch.allocator, "%.*s%.*s", DQN_STR_FMT(file_stem), DQN_STR_FMT(compile_file_obj_suffix)); + } + + Dqn_Str8 obj_file_path = obj_file_name; + if (build_context.build_dir.size) + obj_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_context.build_dir), DQN_STR_FMT(obj_file_name)); + + Dqn_FsInfo file_info = Dqn_Fs_GetInfo(file.input_file_path); + Dqn_FsInfo obj_file_info = Dqn_Fs_GetInfo(obj_file_path); + if (obj_file_info.last_write_time_in_s >= file_info.last_write_time_in_s) + continue; + + dirtied_compile_files.data[dirtied_compile_files.size++] = file; + } + + if (dirtied_compile_files.size <= 0) + return result; + } + + // NOTE: Build the command line invocation ===================================================== + Dqn_Str8Builder builder = {}; + builder.allocator = allocator; + DQN_FOR_UINDEX (index, build_context.compile_flags.size) { + Dqn_Str8 flag = build_context.compile_flags.data[index]; + if (index) + Dqn_Str8Builder_AppendF(&builder, " "); + Dqn_Str8Builder_AppendRef(&builder, flag); + } + + DQN_FOR_UINDEX (index, build_context.include_dirs.size) { + Dqn_Str8 include_dir = build_context.include_dirs.data[index]; + if (builder.count) + Dqn_Str8Builder_AppendF(&builder, " "); + Dqn_Str8Builder_AppendF(&builder, "/I %.*s", DQN_STR_FMT(include_dir)); + } + + DQN_FOR_UINDEX (index, dirtied_compile_files.size) { + Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index]; + Dqn_Str8 obj_file = {}; + if (builder.count) + Dqn_Str8Builder_AppendF(&builder, " "); + + if (file.output_file_path.size) { + switch (build_context.compiler) { + case Dqn_CPPBuildCompiler_MSVC: { + Dqn_Str8Builder_AppendF(&builder, "/Fo%.*s ", DQN_STR_FMT(file.output_file_path)); + } break; + + case Dqn_CPPBuildCompiler_GCC: { + Dqn_Str8Builder_AppendF(&builder, "-o %.*s ", DQN_STR_FMT(file.output_file_path)); + } break; + } + } + + DQN_FOR_UINDEX (flag_index, file.flags.size) { + Dqn_Str8 flag = file.flags.data[flag_index]; + Dqn_Str8Builder_AppendF(&builder, "%s%.*s", flag_index ? " " : "", DQN_STR_FMT(flag)); + } + + if (file.flags.size) + Dqn_Str8Builder_AppendF(&builder, " "); + Dqn_Str8Builder_AppendRef(&builder, file.input_file_path); + } + + DQN_FOR_UINDEX (index, build_context.link_flags.size) { + Dqn_Str8 file = build_context.link_flags.data[index]; + if (builder.count) + Dqn_Str8Builder_AppendF(&builder, " "); + Dqn_Str8Builder_AppendRef(&builder, file); + } + + result = Dqn_Str8Builder_Build(&builder, allocator); + return result; +} + + +DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode) +{ + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator); + Dqn_CPPBuildAsyncResult result = {}; + if (!cmd.size) + return result; + + if (!Dqn_Fs_MakeDir(build_context.build_dir)) { + result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade; + return result; + } + + result.async_handle = Dqn_OS_ExecAsync(cmd, build_context.build_dir); + return result; +} + +void Dqn_CPPBuild_ExecOrAbort(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode) +{ + if (!Dqn_Fs_MakeDir(build_context.build_dir)) { + Dqn_Log_ErrorF("Failed to make build dir '%.*s'", DQN_STR_FMT(build_context.build_dir)); + exit(-1); + } + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator); + Dqn_OS_ExecOrAbort(cmd, build_context.build_dir); +} +#endif // DQN_CPP_BUILD_IMPLEMENTATION diff --git a/dqn_debug.cpp b/dqn_debug.cpp index 7189ce1..ffc2cd1 100644 --- a/dqn_debug.cpp +++ b/dqn_debug.cpp @@ -42,7 +42,7 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_WinError error = Dqn_Win_LastError(scratch.arena); - Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STRING_FMT(error.msg)); + Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg)); } } @@ -132,16 +132,16 @@ DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn // NOTE: Construct result ====================================================================== - Dqn_String16 file_name16 = Dqn_String16{line.FileName, Dqn_CString16_Size(line.FileName)}; - Dqn_String16 function_name16 = Dqn_String16{symbol->Name, symbol->NameLen}; + Dqn_Str16 file_name16 = Dqn_Str16{line.FileName, Dqn_CStr16_Size(line.FileName)}; + Dqn_Str16 function_name16 = Dqn_Str16{symbol->Name, symbol->NameLen}; - Dqn_StackTraceFrame result = {}; - result.address = raw_frame.base_addr; - result.line_number = line.LineNumber; - result.file_name = Dqn_Win_String16ToString8(arena, file_name16); - result.function_name = Dqn_Win_String16ToString8(arena, function_name16); + Dqn_StackTraceFrame result = {}; + result.address = raw_frame.base_addr; + result.line_number = line.LineNumber; + result.file_name = Dqn_Win_Str16ToStr8(arena, file_name16); + result.function_name = Dqn_Win_Str16ToStr8(arena, function_name16); #else - Dqn_StackTraceFrame result = {}; + Dqn_StackTraceFrame result = {}; #endif return result; } @@ -168,12 +168,12 @@ DQN_API void Dqn_StackTrace_Print(uint16_t limit) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Slice stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit); for (Dqn_StackTraceFrame& frame : stack_trace) - Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STRING_FMT(frame.file_name), frame.line_number, DQN_STRING_FMT(frame.function_name)); + Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name)); } // NOTE: [$DEBG] Dqn_Debug ========================================================================= #if defined(DQN_LEAK_TRACING) -DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted) +DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted) { if (!ptr) return; @@ -202,9 +202,9 @@ DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key); if (alloc) { if ((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0) { - Dqn_String8 alloc_stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size); - Dqn_String8 alloc_clean_stack_trace = Dqn_String8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size); - Dqn_String8 clean_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); + Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); + Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size); + Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); DQN_HARD_ASSERTF( alloc->flags & Dqn_AllocRecordFlag_Freed, "\n\nThis pointer is already in the leak tracker, however it has not " @@ -221,8 +221,8 @@ DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize "%.*s" , ptr, alloc->size, - DQN_STRING_FMT(alloc_clean_stack_trace), - DQN_STRING_FMT(clean_stack_trace)); + DQN_STR_FMT(alloc_clean_stack_trace), + DQN_STR_FMT(clean_stack_trace)); } // NOTE: Pointer was reused, clean up the prior entry @@ -243,7 +243,7 @@ DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize alloc->flags |= Dqn_AllocRecordFlag_LeakPermitted; } -DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr) +DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr) { if (!ptr || g_dqn_library->alloc_tracking_disabled) return; @@ -261,13 +261,13 @@ DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr) ptr); if (alloc->flags & Dqn_AllocRecordFlag_Freed) { - Dqn_String8 alloc_stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size); - Dqn_String8 alloc_clean_stack_trace = Dqn_String8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size); + Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); + Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size); - Dqn_String8 alloc_freed_stack_trace = Dqn_String8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size); - Dqn_String8 alloc_freed_clean_stack_trace = Dqn_String8_Slice(alloc_freed_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_freed_stack_trace.size); + Dqn_Str8 alloc_freed_stack_trace = Dqn_Str8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size); + Dqn_Str8 alloc_freed_clean_stack_trace = Dqn_Str8_Slice(alloc_freed_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_freed_stack_trace.size); - Dqn_String8 dealloc_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); + Dqn_Str8 dealloc_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); DQN_HARD_ASSERTF((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0, "\n\nDouble free detected, pointer to free was already marked " @@ -286,9 +286,9 @@ DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr) "%.*s" , ptr, alloc->freed_size, - DQN_STRING_FMT(alloc_clean_stack_trace), - DQN_STRING_FMT(alloc_freed_clean_stack_trace), - DQN_STRING_FMT(dealloc_stack_trace)); + DQN_STR_FMT(alloc_clean_stack_trace), + DQN_STR_FMT(alloc_freed_clean_stack_trace), + DQN_STR_FMT(dealloc_stack_trace)); } alloc->flags |= Dqn_AllocRecordFlag_Freed; @@ -310,12 +310,12 @@ DQN_API void Dqn_Debug_DumpLeaks() leaked_bytes += alloc->size; leak_count++; - Dqn_String8 stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size); - Dqn_String8 clean_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); + Dqn_Str8 stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); + Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); Dqn_Log_WarningF("Pointer (0x%p) leaked %_$$zu at:\n" "%.*s", alloc->ptr, alloc->size, - DQN_STRING_FMT(clean_stack_trace)); + DQN_STR_FMT(clean_stack_trace)); } } diff --git a/dqn_debug.h b/dqn_debug.h index ba765ca..879b25b 100644 --- a/dqn_debug.h +++ b/dqn_debug.h @@ -63,10 +63,10 @@ DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize s struct Dqn_StackTraceFrame { - uint64_t address; - uint64_t line_number; - Dqn_String8 file_name; - Dqn_String8 function_name; + uint64_t address; + uint64_t line_number; + Dqn_Str8 file_name; + Dqn_Str8 function_name; }; struct Dqn_StackTraceRawFrame @@ -113,17 +113,17 @@ struct Dqn_AllocRecord uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag` char padding[2]; }; -static_assert(sizeof(Dqn_AllocRecord) == 48, +static_assert(sizeof(Dqn_AllocRecord) == 48 || sizeof(Dqn_AllocRecord) == 28, // NOTE: 64 bit vs 32 bit pointers respectively "We aim to keep the allocation record as light as possible as " "memory tracking can get expensive. Enforce that there is no " "unexpected padding."); #if defined(DQN_LEAK_TRACING) -#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted) -#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr) +#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted) +#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr) -DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted); -DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr); +DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted); +DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr); DQN_API void Dqn_Debug_DumpLeaks(); #else #define Dqn_Debug_TrackAlloc(...) diff --git a/dqn_external.h b/dqn_external.h index d08eb49..4f47476 100644 --- a/dqn_external.h +++ b/dqn_external.h @@ -1,16 +1,21 @@ // NOTE: [$OS_H] OS Headers ======================================================================== -#if defined(DQN_OS_UNIX) +#if defined(DQN_OS_UNIX) || defined(DQN_PLATFORM_EMSCRIPTEN) #include // errno #include // O_RDONLY ... etc - #include // FICLONE #include // ioctl #include // pid_t + #include // waitpid #include // getrandom #include // stat - #include // sendfile #include // mmap #include // clock_gettime, nanosleep - #include // access, gettid + #include // access, gettid, write + + #if defined(DQN_PLATFORM_EMSCRIPTEN) + #else + #include // sendfile + #include // FICLONE + #endif #endif // NOTE: [$STBS] stb_sprintf ======================================================================= diff --git a/dqn_helpers.cpp b/dqn_helpers.cpp index 42d2546..7d032e3 100644 --- a/dqn_helpers.cpp +++ b/dqn_helpers.cpp @@ -1,3 +1,79 @@ +// NOTE: [$PCGX] Dqn_PCG32 ========================================================================= +#define DQN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL +#define DQN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL + +DQN_API uint32_t Dqn_PCG32_Next(Dqn_PCG32 *rng) +{ + uint64_t state = rng->state; + rng->state = state * DQN_PCG_DEFAULT_MULTIPLIER_64 + DQN_PCG_DEFAULT_INCREMENT_64; + + // XSH-RR + uint32_t value = (uint32_t)((state ^ (state >> 18)) >> 27); + int rot = state >> 59; + return rot ? (value >> rot) | (value << (32 - rot)) : value; +} + +DQN_API uint64_t Dqn_PCG32_Next64(Dqn_PCG32 *rng) +{ + uint64_t value = Dqn_PCG32_Next(rng); + value <<= 32; + value |= Dqn_PCG32_Next(rng); + return value; +} + +DQN_API uint32_t Dqn_PCG32_Range(Dqn_PCG32 *rng, uint32_t low, uint32_t high) +{ + uint32_t bound = high - low; + uint32_t threshold = -(int32_t)bound % bound; + + for (;;) { + uint32_t r = Dqn_PCG32_Next(rng); + if (r >= threshold) + return low + (r % bound); + } +} + +DQN_API float Dqn_PCG32_NextF32(Dqn_PCG32 *rng) +{ + uint32_t x = Dqn_PCG32_Next(rng); + return (float)(int32_t)(x >> 8) * 0x1.0p-24f; +} + +DQN_API double Dqn_PCG32_NextF64(Dqn_PCG32 *rng) +{ + uint64_t x = Dqn_PCG32_Next64(rng); + return (double)(int64_t)(x >> 11) * 0x1.0p-53; +} + +DQN_API void Dqn_PCG32_Seed(Dqn_PCG32 *rng, uint64_t seed) +{ + rng->state = 0ULL; + Dqn_PCG32_Next(rng); + rng->state += seed; + Dqn_PCG32_Next(rng); +} + +DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta) +{ + uint64_t cur_mult = DQN_PCG_DEFAULT_MULTIPLIER_64; + uint64_t cur_plus = DQN_PCG_DEFAULT_INCREMENT_64; + + uint64_t acc_mult = 1; + uint64_t acc_plus = 0; + + while (delta != 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta >>= 1; + } + + rng->state = acc_mult * rng->state + acc_plus; +} + #if !defined(DQN_NO_JSON_BUILDER) // NOTE: [$JSON] Dqn_JSONBuilder =================================================================== DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces_per_indent) @@ -8,13 +84,13 @@ DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces return result; } -DQN_API Dqn_String8 Dqn_JSONBuilder_Build(Dqn_JSONBuilder const *builder, Dqn_Allocator allocator) +DQN_API Dqn_Str8 Dqn_JSONBuilder_Build(Dqn_JSONBuilder const *builder, Dqn_Allocator allocator) { - Dqn_String8 result = Dqn_String8Builder_Build(&builder->string_builder, allocator); + Dqn_Str8 result = Dqn_Str8Builder_Build(&builder->string_builder, allocator); return result; } -DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value) +DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value) { if (key.size == 0 && value.size == 0) return; @@ -47,18 +123,18 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_String8 key, int spaces = builder->indent_level * spaces_per_indent; if (key.size) { - Dqn_String8Builder_AppendF(&builder->string_builder, + Dqn_Str8Builder_AppendF(&builder->string_builder, "%.*s%*c\"%.*s\": %.*s", prefix_size, prefix, spaces, ' ', - DQN_STRING_FMT(key), - DQN_STRING_FMT(value)); + DQN_STR_FMT(key), + DQN_STR_FMT(value)); } else { - Dqn_String8Builder_AppendF(&builder->string_builder, + Dqn_Str8Builder_AppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', - DQN_STRING_FMT(value)); + DQN_STR_FMT(value)); } if (item == Dqn_JSONBuilderItem_OpenContainer) @@ -67,14 +143,14 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_String8 key, builder->last_item = item; } -DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, va_list args) +DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, va_list args) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(builder->string_builder.allocator.user_context); - Dqn_String8 value = Dqn_String8_InitFV(scratch.allocator, value_fmt, args); + Dqn_Str8 value = Dqn_Str8_InitFV(scratch.allocator, value_fmt, args); Dqn_JSONBuilder_KeyValue(builder, key, value); } -DQN_API void Dqn_JSONBuilder_KeyValueF(Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, ...) +DQN_API void Dqn_JSONBuilder_KeyValueF(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...) { va_list args; va_start(args, value_fmt); @@ -82,47 +158,47 @@ DQN_API void Dqn_JSONBuilder_KeyValueF(Dqn_JSONBuilder *builder, Dqn_String8 key va_end(args); } -DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name) +DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_Str8 name) { - Dqn_JSONBuilder_KeyValue(builder, name, DQN_STRING8("{")); + Dqn_JSONBuilder_KeyValue(builder, name, DQN_STR8("{")); } DQN_API void Dqn_JSONBuilder_ObjectEnd(Dqn_JSONBuilder *builder) { - Dqn_JSONBuilder_KeyValue(builder, DQN_STRING8(""), DQN_STRING8("}")); + Dqn_JSONBuilder_KeyValue(builder, DQN_STR8(""), DQN_STR8("}")); } -DQN_API void Dqn_JSONBuilder_ArrayBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name) +DQN_API void Dqn_JSONBuilder_ArrayBeginNamed(Dqn_JSONBuilder *builder, Dqn_Str8 name) { - Dqn_JSONBuilder_KeyValue(builder, name, DQN_STRING8("[")); + Dqn_JSONBuilder_KeyValue(builder, name, DQN_STR8("[")); } DQN_API void Dqn_JSONBuilder_ArrayEnd(Dqn_JSONBuilder *builder) { - Dqn_JSONBuilder_KeyValue(builder, DQN_STRING8(""), DQN_STRING8("]")); + Dqn_JSONBuilder_KeyValue(builder, DQN_STR8(""), DQN_STR8("]")); } -DQN_API void Dqn_JSONBuilder_StringNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value) +DQN_API void Dqn_JSONBuilder_StrNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value) { Dqn_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data); } -DQN_API void Dqn_JSONBuilder_LiteralNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value) +DQN_API void Dqn_JSONBuilder_LiteralNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value) { Dqn_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data); } -DQN_API void Dqn_JSONBuilder_U64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, uint64_t value) +DQN_API void Dqn_JSONBuilder_U64Named(Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value) { Dqn_JSONBuilder_KeyValueF(builder, key, "%I64u", value); } -DQN_API void Dqn_JSONBuilder_I64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, int64_t value) +DQN_API void Dqn_JSONBuilder_I64Named(Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value) { Dqn_JSONBuilder_KeyValueF(builder, key, "%I64d", value); } -DQN_API void Dqn_JSONBuilder_F64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, double value, int decimal_places) +DQN_API void Dqn_JSONBuilder_F64Named(Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places) { if (!builder) return; @@ -150,9 +226,9 @@ DQN_API void Dqn_JSONBuilder_F64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_JSONBuilder_KeyValueF(builder, key, fmt, value); } -DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, bool value) +DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value) { - Dqn_String8 value_string = value ? DQN_STRING8("true") : DQN_STRING8("false"); + Dqn_Str8 value_string = value ? DQN_STR8("true") : DQN_STR8("false"); Dqn_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data); } #endif // !defined(DQN_NO_JSON_BUILDER) @@ -161,32 +237,32 @@ DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_String8 key // NOTE: [$BHEX] Dqn_Bin =========================================================================== DQN_API char const *Dqn_Bin_HexBufferTrim0x(char const *hex, Dqn_usize size, Dqn_usize *real_size) { - Dqn_String8 result = Dqn_String8_TrimWhitespaceAround(Dqn_String8_Init(hex, size)); - result = Dqn_String8_TrimPrefix(result, DQN_STRING8("0x"), Dqn_String8EqCase_Insensitive); + Dqn_Str8 result = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(hex, size)); + result = Dqn_Str8_TrimPrefix(result, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive); if (real_size) *real_size = result.size; return result.data; } -DQN_API Dqn_String8 Dqn_Bin_HexTrim0x(Dqn_String8 string) +DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x(Dqn_Str8 string) { - Dqn_usize trimmed_size = 0; - char const *trimmed = Dqn_Bin_HexBufferTrim0x(string.data, string.size, &trimmed_size); - Dqn_String8 result = Dqn_String8_Init(trimmed, trimmed_size); + Dqn_usize trimmed_size = 0; + char const *trimmed = Dqn_Bin_HexBufferTrim0x(string.data, string.size, &trimmed_size); + Dqn_Str8 result = Dqn_Str8_Init(trimmed, trimmed_size); return result; } -DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String(uint64_t number, uint32_t flags) +DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8(uint64_t number, uint32_t flags) { - Dqn_String8 prefix = {}; - if (!(flags & Dqn_BinHexU64StringFlags_No0xPrefix)) - prefix = DQN_STRING8("0x"); + Dqn_Str8 prefix = {}; + if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + prefix = DQN_STR8("0x"); - Dqn_BinHexU64String result = {}; + Dqn_BinHexU64Str8 result = {}; DQN_MEMCPY(result.data, prefix.data, prefix.size); result.size += DQN_CAST(int8_t)prefix.size; - char const *fmt = (flags & Dqn_BinHexU64StringFlags_UppercaseHex) ? "%I64X" : "%I64x"; + char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; int size = STB_SPRINTF_DECORATE(snprintf)(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number); result.size += DQN_CAST(uint8_t)size; DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data)); @@ -198,17 +274,17 @@ DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String(uint64_t number, uint32_t return result; } -DQN_API Dqn_String8 Dqn_Bin_U64ToHex(Dqn_Allocator allocator, uint64_t number, uint32_t flags) +DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Allocator allocator, uint64_t number, uint32_t flags) { - Dqn_String8 prefix = {}; - if (!(flags & Dqn_BinHexU64StringFlags_No0xPrefix)) - prefix = DQN_STRING8("0x"); + Dqn_Str8 prefix = {}; + if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + prefix = DQN_STR8("0x"); - char const *fmt = (flags & Dqn_BinHexU64StringFlags_UppercaseHex) ? "%I64X" : "%I64x"; - Dqn_usize required_size = Dqn_CString8_FSize(fmt, number) + prefix.size; - Dqn_String8 result = Dqn_String8_Allocate(allocator, required_size, Dqn_ZeroMem_No); + char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; + Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size; + Dqn_Str8 result = Dqn_Str8_Allocate(allocator, required_size, Dqn_ZeroMem_No); - if (Dqn_String8_IsValid(result)) { + if (Dqn_Str8_IsValid(result)) { DQN_MEMCPY(result.data, prefix.data, prefix.size); int space = DQN_CAST(int)DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/ STB_SPRINTF_DECORATE(snprintf)(result.data + prefix.size, space, fmt, number); @@ -241,7 +317,7 @@ DQN_API uint64_t Dqn_Bin_HexBufferToU64(char const *hex, Dqn_usize size) return result; } -DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_String8 hex) +DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_Str8 hex) { uint64_t result = Dqn_Bin_HexBufferToU64(hex.data, hex.size); return result; @@ -279,9 +355,9 @@ DQN_API char *Dqn_Bin_BytesToHexBufferArena(Dqn_Arena *arena, void const *src, D return result; } -DQN_API Dqn_String8 Dqn_Bin_BytesToHexArena(Dqn_Arena *arena, void const *src, Dqn_usize size) +DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena(Dqn_Arena *arena, void const *src, Dqn_usize size) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; result.data = Dqn_Bin_BytesToHexBufferArena(arena, src, size); if (result.data) result.size = size * 2; @@ -346,13 +422,13 @@ DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize h return result; } -DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked(Dqn_String8 hex, void *dest, Dqn_usize dest_size) +DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked(Dqn_Str8 hex, void *dest, Dqn_usize dest_size) { Dqn_usize result = Dqn_Bin_HexBufferToBytesUnchecked(hex.data, hex.size, dest, dest_size); return result; } -DQN_API Dqn_usize Dqn_Bin_HexToBytes(Dqn_String8 hex, void *dest, Dqn_usize dest_size) +DQN_API Dqn_usize Dqn_Bin_HexToBytes(Dqn_Str8 hex, void *dest, Dqn_usize dest_size) { Dqn_usize result = Dqn_Bin_HexBufferToBytes(hex.data, hex.size, dest, dest_size); return result; @@ -364,13 +440,10 @@ DQN_API char *Dqn_Bin_HexBufferToBytesArena(Dqn_Arena *arena, char const *hex, D if (!arena || !hex || size <= 0) return result; - Dqn_usize trim_size = 0; - char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, - size, - &trim_size); - - Dqn_usize binary_size = trim_size / 2; - result = Dqn_Arena_NewArray(arena, char, binary_size, Dqn_ZeroMem_No); + Dqn_usize trim_size = 0; + char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, size, &trim_size); + Dqn_usize binary_size = trim_size / 2; + result = Dqn_Arena_NewArray(arena, char, binary_size, Dqn_ZeroMem_No); if (result) { Dqn_usize convert_size = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, result, binary_size); if (real_size) @@ -379,10 +452,10 @@ DQN_API char *Dqn_Bin_HexBufferToBytesArena(Dqn_Arena *arena, char const *hex, D return result; } -DQN_API Dqn_String8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_String8 hex) +DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_Str8 hex) { - Dqn_String8 result = {}; - result.data = Dqn_Bin_HexBufferToBytesArena(arena, hex.data, hex.size, &result.size); + Dqn_Str8 result = {}; + result.data = Dqn_Bin_HexBufferToBytesArena(arena, hex.data, hex.size, &result.size); return result; } #endif // !defined(DQN_NO_BIN) @@ -505,7 +578,7 @@ DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val) DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val) { - uint64_t result = DQN_CHECK(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX; + uint64_t result = DQN_CHECK(DQN_CAST(uint64_t)val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX; return result; } @@ -572,8 +645,8 @@ DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val) DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val) { - DQN_ASSERT(val >= INT64_MIN && val <= INT64_MAX); - int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX); + DQN_ASSERT(DQN_CAST(int64_t)val >= INT64_MIN && DQN_CAST(int64_t)val <= INT64_MAX); + int64_t result = DQN_CAST(int64_t)DQN_CLAMP(DQN_CAST(int64_t)val, INT64_MIN, INT64_MAX); return result; } @@ -733,7 +806,7 @@ DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val) } // NOTE: [$MISC] Misc ============================================================================== -DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ...) +DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -747,16 +820,16 @@ DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ... return result; } -DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator) +DQN_API Dqn_U64Str8 Dqn_U64ToStr8(uint64_t val, char separator) { - Dqn_U64String result = {}; + Dqn_U64Str8 result = {}; if (val == 0) { result.data[result.size++] = '0'; } else { // NOTE: The number is written in reverse because we form the string by // dividing by 10, so we write it in, then reverse it out after all is // done. - Dqn_U64String temp = {}; + Dqn_U64Str8 temp = {}; for (Dqn_usize digit_count = 0; val > 0; digit_count++) { if (separator && (digit_count != 0) && (digit_count % 3 == 0)) temp.data[temp.size++] = separator; @@ -783,7 +856,7 @@ DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator) // NOTE: [$DLIB] Dqn_Library ======================================================================= Dqn_Library *g_dqn_library; -DQN_API Dqn_Library *Dqn_Library_Init() +DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) { if (!g_dqn_library) { static Dqn_Library default_instance = {}; @@ -801,8 +874,8 @@ DQN_API Dqn_Library *Dqn_Library_Init() // NOTE: Query OS page size ==================================================================== { - SYSTEM_INFO system_info = {}; #if defined(DQN_OS_WIN32) + SYSTEM_INFO system_info = {}; GetSystemInfo(&system_info); result->os_page_size = system_info.dwPageSize; result->os_alloc_granularity = system_info.dwAllocationGranularity; @@ -829,8 +902,8 @@ DQN_API Dqn_Library *Dqn_Library_Init() { result->alloc_tracking_disabled = true; // TODO(doyle): @robust Does this need to be atomic? - Dqn_String8 sample_backtrace = Dqn_String8_InitCString8(b_stacktrace_get_string()); - Dqn_String8 clean_backtrace = Dqn_Debug_CleanStackTrace(sample_backtrace); + Dqn_Str8 sample_backtrace = Dqn_Str8_InitCStr8(b_stacktrace_get_string()); + Dqn_Str8 clean_backtrace = Dqn_Debug_CleanStackTrace(sample_backtrace); result->stack_trace_offset_to_our_call_stack = DQN_CAST(uint16_t)(sample_backtrace.size - clean_backtrace.size); free(sample_backtrace.data); @@ -840,28 +913,34 @@ DQN_API Dqn_Library *Dqn_Library_Init() #endif // NOTE: Print out init features =============================================================== + if (on_init == Dqn_LibraryOnInit_LogFeatures) { + Dqn_Log_DebugF("Dqn Library initialised:\n"); - Dqn_Log_DebugF("Dqn Library initialised:\n"); - Dqn_Print_StdLnF(Dqn_PrintStd_Err, " OS Page Size/Alloc Granularity: %$$_I32u/%$$_I32u", result->os_page_size, result->os_alloc_granularity); + // NOTE: %$$_I32u is a stb_sprintf format specifier, non-standard + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'Dqn_Print_StdLnF'. + Dqn_Print_StdLnF(Dqn_PrintStd_Err, " OS Page Size/Alloc Granularity: %$$_I32u/%$$_I32u", result->os_page_size, result->os_alloc_granularity); + DQN_MSVC_WARNING_POP - #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) - if (DQN_ASAN_POISON) { - Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN manual poisoning%s", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : ""); - Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN poison guard size: %$$_I32u", DQN_ASAN_POISON_GUARD_SIZE); + #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) + if (DQN_ASAN_POISON) { + Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN manual poisoning%s", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : ""); + Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN poison guard size: %$$_I32u", DQN_ASAN_POISON_GUARD_SIZE); + } + #endif + + #if defined(DQN_LEAK_TRACING) + Dqn_Print_StdLnF(Dqn_PrintStd_Err, " Allocation leak tracing"); + #endif + + #if !defined(DQN_NO_PROFILER) + Dqn_Print_StdLnF(Dqn_PrintStd_Err, " TSC profiler available"); + #endif + + // TODO(doyle): Add stacktrace feature log + + Dqn_Print_StdLnF(Dqn_PrintStd_Err, ""); } - #endif - - #if defined(DQN_LEAK_TRACING) - Dqn_Print_StdLnF(Dqn_PrintStd_Err, " Allocation leak tracing"); - #endif - - #if !defined(DQN_NO_PROFILER) - Dqn_Print_StdLnF(Dqn_PrintStd_Err, " TSC profiler available"); - #endif - - // TODO(doyle): Add stacktrace feature log - - Dqn_Print_StdLnF(Dqn_PrintStd_Err, ""); return result; } @@ -885,14 +964,14 @@ DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data) g_dqn_library->log_user_data = user_data; } -DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path) +DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path) { #if defined(DQN_DEBUG_THREAD_CONTEXT) // NOTE: Open a file to write the arena stats to FILE *file = nullptr; fopen_s(&file, file_path.data, "a+b"); if (file) { - Dqn_Log_ErrorF("Failed to dump thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path)); + Dqn_Log_ErrorF("Failed to dump thread context arenas [file=%.*s]", DQN_STR_FMT(file_path)); return; } @@ -908,7 +987,7 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path) Dqn_TicketMutex_End(&g_dqn_library->thread_context_mutex); // NOTE: Print the cumulative stat - Dqn_DateHMSTimeString now = Dqn_Date_HMSLocalTimeStringNow(); + Dqn_DateHMSTimeStr now = Dqn_Date_HMSLocalTimeStrNow(); fprintf(file, "Time=%.*s %.*s | Thread Context Arenas | Count=%d\n", now.date_size, now.date, @@ -931,19 +1010,19 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path) stat.blocks_hwm = DQN_MAX(stat.blocks_hwm, current->blocks_hwm); } - Dqn_ArenaStatString stats_string = Dqn_Arena_StatString(&stat); + Dqn_ArenaStatStr stats_string = Dqn_Arena_StatStr(&stat); fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data); } // NOTE: Print individual thread arena data for (Dqn_usize index = 0; index < stats_size; index++) { Dqn_ArenaStat const *current = stats + index; - Dqn_ArenaStatString current_string = Dqn_Arena_StatString(current); + Dqn_ArenaStatStr current_string = Dqn_Arena_StatStr(current); fprintf(file, " [%03d] CURR %.*s\n", DQN_CAST(int)index, current_string.size, current_string.data); } fclose(file); - Dqn_Log_InfoF("Dumped thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path)); + Dqn_Log_InfoF("Dumped thread context arenas [file=%.*s]", DQN_STR_FMT(file_path)); #else (void)file_path; #endif // #if defined(DQN_DEBUG_THREAD_CONTEXT) @@ -952,7 +1031,7 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path) #if !defined(DQN_NO_PROFILER) // NOTE: [$PROF] Dqn_Profiler ====================================================================== -Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_String8 name, uint16_t anchor_index) +Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index) { zone = Dqn_Profiler_BeginZoneWithIndex(name, anchor_index); } @@ -962,7 +1041,7 @@ Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope() Dqn_Profiler_EndZone(zone); } -Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_String8 name, uint16_t anchor_index) +Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index) { Dqn_ProfilerAnchor *anchor = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back) + anchor_index; anchor->name = name; @@ -998,11 +1077,11 @@ Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer) return result; } -void Dqn_Profiler_SwapAnchorBuffer(uint32_t anchor_count) +void Dqn_Profiler_SwapAnchorBuffer() { g_dqn_library->profiler->active_anchor_buffer++; Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); - DQN_MEMSET(anchors, 0, anchor_count * sizeof(g_dqn_library->profiler->anchors[0][0])); + DQN_MEMSET(anchors, 0, DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0])); } void Dqn_Profiler_Dump(uint64_t tsc_per_second) @@ -1018,13 +1097,13 @@ void Dqn_Profiler_Dump(uint64_t tsc_per_second) Dqn_f64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DQN_CAST(Dqn_f64)tsc_per_second; if (tsc_exclusive == tsc_inclusive) { Dqn_Print_LnF("%.*s[%u]: %.1fms", - DQN_STRING_FMT(anchor->name), + DQN_STR_FMT(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds); } else { Dqn_f64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DQN_CAST(Dqn_f64)tsc_per_second; Dqn_Print_LnF("%.*s[%u]: %.1f/%.1fms", - DQN_STRING_FMT(anchor->name), + DQN_STR_FMT(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds, tsc_inclusive_milliseconds); diff --git a/dqn_helpers.h b/dqn_helpers.h index c76d533..acb0078 100644 --- a/dqn_helpers.h +++ b/dqn_helpers.h @@ -1,3 +1,25 @@ +// NOTE: [$PCGX] Dqn_PCG32 ========================================================================= +// Random number generator of the PCG family. Implementation taken from Martins +// Mmozeiko from Handmade Network. +// https://gist.github.com/mmozeiko/1561361cd4105749f80bb0b9223e9db8 +// +// NOTE: API ======================================================================================= +// @proc Dqn_PCG32_Range +// @desc Returns a value in the [low; high) interval + +// @proc Dqn_PCG32_NextF +// @desc Returns a float & double in [0; 1) interval; + +struct Dqn_PCG32 { uint64_t state; }; + +DQN_API uint32_t Dqn_PCG32_Next (Dqn_PCG32 *rng); +DQN_API uint64_t Dqn_PCG32_Next64 (Dqn_PCG32 *rng); +DQN_API uint32_t Dqn_PCG32_Range (Dqn_PCG32 *rng, uint32_t low, uint32_t high); +DQN_API Dqn_f32 Dqn_PCG32_NextF32(Dqn_PCG32 *rng); +DQN_API Dqn_f64 Dqn_PCG32_NextF64(Dqn_PCG32 *rng); +DQN_API void Dqn_PCG32_Seed (Dqn_PCG32 *rng, uint64_t seed); +DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta); + #if !defined(DQN_NO_JSON_BUILDER) // NOTE: [$JSON] Dqn_JSONBuilder =================================================================== // Basic helper class to construct JSON output to a string @@ -44,7 +66,7 @@ enum Dqn_JSONBuilderItem struct Dqn_JSONBuilder { bool use_stdout; // When set, ignore the string builder and dump immediately to stdout - Dqn_String8Builder string_builder; // (Internal) + Dqn_Str8Builder string_builder; // (Internal) int indent_level; // (Internal) int spaces_per_indent; // The number of spaces per indent level Dqn_JSONBuilderItem last_item; @@ -68,23 +90,23 @@ struct Dqn_JSONBuilder DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Allocator allocator, int spaces_per_indent); -DQN_API Dqn_String8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator); -DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value); -DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, ...); -DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name); +DQN_API Dqn_Str8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator); +DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value); +DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...); +DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_Str8 name); DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder); -DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_String8 name); +DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name); DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder); -DQN_API void Dqn_JSONBuilder_StringNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value); -DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value); -DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, uint64_t value); -DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, int64_t value); -DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, double value, int decimal_places); -DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, bool value); +DQN_API void Dqn_JSONBuilder_Str8Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value); +DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value); +DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value); +DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value); +DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places); +DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value); #define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STRING8("")) #define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STRING8("")) -#define Dqn_JSONBuilder_String(builder, value) Dqn_JSONBuilder_StringNamed(builder, DQN_STRING8(""), value) +#define Dqn_JSONBuilder_Str8(builder, value) Dqn_JSONBuilder_Str8Named(builder, DQN_STRING8(""), value) #define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STRING8(""), value) #define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STRING8(""), value) #define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STRING8(""), value) @@ -95,20 +117,20 @@ DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builde #if !defined(DQN_NO_BIN) // NOTE: [$BHEX] Dqn_Bin =========================================================================== // NOTE: API ======================================================================================= -// @proc Dqn_Bin_U64ToHexU64String +// @proc Dqn_Bin_U64ToHexU64Str8 // @desc Convert a 64 bit number to a hex string // @param[in] number Number to convert to hexadecimal representation -// @param[in] flags Bit flags from Dqn_BinHexU64StringFlags to customise the +// @param[in] flags Bit flags from Dqn_BinHexU64Str8Flags to customise the // output of the hexadecimal string. // @return The hexadecimal representation of the number. This string is always // null-terminated. // @proc Dqn_Bin_U64ToHex -// @copybrief Dqn_Bin_U64ToHexU64String +// @copybrief Dqn_Bin_U64ToHexU64Str8 // @param[in] allocator The memory allocator to use for the memory of the // hexadecimal string. -// @copyparams Dqn_Bin_U64ToHexU64String +// @copyparams Dqn_Bin_U64ToHexU64Str8 // @proc Dqn_Bin_HexBufferToU64 // @desc Convert a hexadecimal string a 64 bit value. @@ -151,17 +173,17 @@ DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builde // be greater than `dest_size`. // @proc Dqn_Bin_HexToBytes -// @desc String8 variant of @see Dqn_Bin_HexBufferToBytes +// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytes -// @proc Dqn_Bin_StringHexBufferToBytesUnchecked +// @proc Dqn_Bin_Str8HexBufferToBytesUnchecked // @desc Unchecked variant of @see Dqn_Bin_HexBufferToBytes // // This function skips some string checks, it assumes the hex is a valid hex // stream and that the arguments are valid e.g. no trimming or 0x prefix // stripping is performed -// @proc Dqn_Bin_String -// @desc String8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked +// @proc Dqn_Bin_Str8 +// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked // @proc Dqn_Bin_HexBufferToBytesArena // Dynamic allocating variant of @see Dqn_Bin_HexBufferToBytesUnchecked @@ -181,28 +203,28 @@ DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builde // // @return The byte representation of the hex string. -struct Dqn_BinHexU64String +struct Dqn_BinHexU64Str8 { char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/]; uint8_t size; }; -enum Dqn_BinHexU64StringFlags +enum Dqn_BinHexU64Str8Flags { - Dqn_BinHexU64StringFlags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string - Dqn_BinHexU64StringFlags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex + Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string + Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex }; DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size); -DQN_API Dqn_String8 Dqn_Bin_HexTrim0x (Dqn_String8 string); +DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string); -DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String (uint64_t number, uint32_t flags); -DQN_API Dqn_String8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags); +DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags); +DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags); DQN_API uint64_t Dqn_Bin_HexBufferToU64 (char const *hex, Dqn_usize size); -DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_String8 hex); +DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex); -DQN_API Dqn_String8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size); +DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size); DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size); DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size); @@ -210,9 +232,9 @@ DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size); DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size); -DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_String8 hex, void *dest, Dqn_usize dest_size); -DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_String8 hex, void *dest, Dqn_usize dest_size); -DQN_API Dqn_String8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_String8 hex); +DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size); +DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size); +DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex); #endif // !defined(DQN_NO_BIN) // NOTE: [$BSEA] Dqn_BinarySearch ================================================================== @@ -220,17 +242,26 @@ template using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); template -DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); +bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); +// TODO(doyle): Implement lower/upper bound searching enum Dqn_BinarySearchType { - /// Index of the match. If no match is found, found is set to false and the - /// index is set to 0 + // Index of the match. If no match is found, found is set to false and the + // index is set to the index where the match should be inserted/exist, if + // it were in the array (in practice this turns out to be the first or + // [last index + 1] of the array). Dqn_BinarySearchType_Match, - /// Index after the match. If no match is found, found is set to false and - /// the index is set to one past the closest match. - Dqn_BinarySearchType_OnePastMatch, + // Index of the first element in the array that is `<= find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + Dqn_BinarySearchType_LowerBound, + + // Index of the first element in the array that is `> find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + Dqn_BinarySearchType_UpperBound, }; struct Dqn_BinarySearchResult @@ -240,15 +271,14 @@ struct Dqn_BinarySearchResult }; template -Dqn_BinarySearchResult -Dqn_BinarySearch(T const *array, - Dqn_usize array_size, - T const &find, - Dqn_BinarySearchType type = Dqn_BinarySearchType_Match, - Dqn_BinarySearchLessThanProc less_than = Dqn_BinarySearch_DefaultLessThan); +Dqn_BinarySearchResult Dqn_BinarySearch(T const *array, + Dqn_usize array_size, + T const &find, + Dqn_BinarySearchType type = Dqn_BinarySearchType_Match, + Dqn_BinarySearchLessThanProc less_than = Dqn_BinarySearch_DefaultLessThan); - -template DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) +template +bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) { bool result = lhs < rhs; return result; @@ -256,37 +286,47 @@ template DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T c template Dqn_BinarySearchResult -Dqn_BinarySearch(T const *array, - Dqn_usize array_size, - T const &find, - Dqn_BinarySearchType type, - Dqn_BinarySearchLessThanProc less_than) +Dqn_BinarySearch(T const *array, + Dqn_usize array_size, + T const &find, + Dqn_BinarySearchType type, + Dqn_BinarySearchLessThanProc less_than) { Dqn_BinarySearchResult result = {}; - Dqn_usize head = 0; - Dqn_usize tail = array_size - 1; - if (array && array_size > 0) { - while (!result.found && head <= tail) { - Dqn_usize mid = (head + tail) / 2; - T const &value = array[mid]; - if (less_than(find, value)) { - tail = mid - 1; - if (mid == 0) - break; - } else if (less_than(value, find)) { - head = mid + 1; - } else { - result.found = true; - result.index = mid; - } - } + if (!array || array_size <= 0) + return result; + + T const *end = array + array_size; + T const *first = array; + T const *last = end; + while (first != last) { + Dqn_usize count = last - first; + T const *it = first + (count / 2); + + bool advance_first = false; + if (type == Dqn_BinarySearchType_UpperBound) + advance_first = !less_than(find, it[0]); + else + advance_first = less_than(it[0], find); + + if (advance_first) + first = it + 1; + else + last = it; } - if (type == Dqn_BinarySearchType_OnePastMatch) - result.index = result.found ? result.index + 1 : tail + 1; - else - DQN_ASSERT(type == Dqn_BinarySearchType_Match); + switch (type) { + case Dqn_BinarySearchType_Match: { + result.found = first != end && !less_than(find, *first); + } break; + case Dqn_BinarySearchType_LowerBound: /*FALLTHRU*/ + case Dqn_BinarySearchType_UpperBound: { + result.found = first != end; + } break; + } + + result.index = first - array; return result; } @@ -453,20 +493,20 @@ DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val); // @return The size of the string written to the buffer *not* including the // null-terminator. // -// @proc Dqn_U64ToString +// @proc Dqn_U64ToStr8 // @desc Convert a 64 bit unsigned value to its string representation. // @param[in] val Value to convert into a string // @param[in] separator The separator to insert every 3 digits. Set this to // 0 if no separator is desired. -struct Dqn_U64String +struct Dqn_U64Str8 { char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor uint8_t size; }; -DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ...); -DQN_API Dqn_U64String Dqn_U64ToString (uint64_t val, char separator); +DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator); #if !defined(DQN_NO_PROFILER) // NOTE: [$PROF] Dqn_Profiler ====================================================================== @@ -524,29 +564,29 @@ struct Dqn_ProfilerAnchor // time spent in children functions that we call that are also being // profiled. If we recursively call into ourselves, the time we spent in // our function is accumulated. - uint64_t tsc_inclusive; - uint64_t tsc_exclusive; - uint16_t hit_count; - Dqn_String8 name; + uint64_t tsc_inclusive; + uint64_t tsc_exclusive; + uint16_t hit_count; + Dqn_Str8 name; }; struct Dqn_ProfilerZone { - uint16_t anchor_index; - uint64_t begin_tsc; - uint16_t parent_zone; - uint64_t elapsed_tsc_at_zone_start; + uint16_t anchor_index; + uint64_t begin_tsc; + uint16_t parent_zone; + uint64_t elapsed_tsc_at_zone_start; }; #if defined(__cplusplus) struct Dqn_ProfilerZoneScope { - Dqn_ProfilerZoneScope(Dqn_String8 name, uint16_t anchor_index); + Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index); ~Dqn_ProfilerZoneScope(); Dqn_ProfilerZone zone; }; -#define Dqn_Profiler_ZoneScope(name) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STRING8(name), __COUNTER__ + 1) -#define Dqn_Profiler_ZoneScopeWithIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STRING8(name), anchor_index) +#define Dqn_Profiler_ZoneScope(name) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), __COUNTER__ + 1) +#define Dqn_Profiler_ZoneScopeWithIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index) #endif enum Dqn_ProfilerAnchorBuffer @@ -566,10 +606,10 @@ struct Dqn_Profiler #define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8(name), __COUNTER__ + 1) // NOTE: API ======================================================================================= -Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_String8 name, uint16_t anchor_index); +Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index); void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone); Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer); -void Dqn_Profiler_SwapAnchorBuffer (uint32_t anchor_count); +void Dqn_Profiler_SwapAnchorBuffer (); void Dqn_Profiler_Dump (uint64_t tsc_per_second); #endif // !defined(DQN_NO_PROFILER) @@ -596,7 +636,7 @@ struct Dqn_Library { bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` Dqn_TicketMutex lib_mutex; - Dqn_String8 exe_dir; // The directory of the current executable + Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Arena arena; Dqn_ArenaCatalog arena_catalog; @@ -642,12 +682,18 @@ struct Dqn_Library #endif } extern *g_dqn_library; +enum Dqn_LibraryOnInit +{ + Dqn_LibraryOnInit_Nil, + Dqn_LibraryOnInit_LogFeatures, +}; + // NOTE: API ======================================================================================= -DQN_API Dqn_Library *Dqn_Library_Init (); +DQN_API Dqn_Library *Dqn_Library_Init (Dqn_LibraryOnInit on_init); DQN_API void Dqn_Library_SetPointer (Dqn_Library *library); #if !defined(DQN_NO_PROFILER) DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler); #endif DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data); -DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path); +DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path); diff --git a/dqn_math.cpp b/dqn_math.cpp index 4d5d2eb..9451115 100644 --- a/dqn_math.cpp +++ b/dqn_math.cpp @@ -43,6 +43,12 @@ DQN_API Dqn_V2I operator-(Dqn_V2I lhs, Dqn_V2I rhs) return result; } +DQN_API Dqn_V2I operator-(Dqn_V2I lhs) +{ + Dqn_V2I result = Dqn_V2I_InitNx2(-lhs.x, -lhs.y); + return result; +} + DQN_API Dqn_V2I operator+(Dqn_V2I lhs, Dqn_V2I rhs) { Dqn_V2I result = Dqn_V2I_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y); @@ -315,6 +321,12 @@ DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_f32 rhs) return result; } +DQN_API Dqn_V2 operator-(Dqn_V2 lhs) +{ + Dqn_V2 result = Dqn_V2_InitNx2(-lhs.x, -lhs.y); + return result; +} + 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); @@ -405,12 +417,24 @@ DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2 rhs) return lhs; } +DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_f32 rhs) +{ + lhs = lhs - rhs; + return lhs; +} + DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2 rhs) { lhs = lhs + rhs; return lhs; } +DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_f32 rhs) +{ + lhs = lhs + rhs; + return lhs; +} + DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b) { Dqn_V2 result = Dqn_V2_InitNx2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y)); @@ -431,31 +455,126 @@ DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a) DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b) { + // NOTE: Scalar projection of B onto A ========================================================= + // + // Scalar projection calculates the signed distance between `b` and `a` + // where `a` is a unit vector then, the dot product calculates the projection + // of `b` onto the infinite line that the direction of `a` represents. This + // calculation is the signed distance. + // + // signed_distance = dot_product(a, b) = (a.x * b.x) + (a.y * b.y) + // + // Y + // ^ b + // | /| + // | / | + // | / | + // | / | Projection + // | / | + // |/ V + // +--->--------> X + // . a . + // . . + // |------| <- Calculated signed distance + // + // The signed-ness of the result indicates the relationship: + // + // Distance <0 means `b` is behind `a` + // Distance >0 means `b` is in-front of `a` + // Distance ==0 means `b` is perpendicular to `a` + // + // If `a` is not normalized then the signed-ness of the result still holds + // however result no longer represents the actual distance between the + // 2 objects. One of the vectors must be normalised (e.g. turned into a unit + // vector). + // + // NOTE: Vector projection ===================================================================== + // + // Vector projection calculates the exact X,Y coordinates of where `b` meets + // `a` when it was projected. This is calculated by multipying the + // 'scalar projection' result by the unit vector of `a` + // + // vector_projection = a * signed_distance = a * dot_product(a, b) + Dqn_f32 result = (a.x * b.x) + (a.y * b.y); return result; } -DQN_API Dqn_f32 Dqn_V2_LengthSq(Dqn_V2 a, Dqn_V2 b) +DQN_API Dqn_f32 Dqn_V2_LengthSq_V2x2(Dqn_V2 lhs, Dqn_V2 rhs) { - Dqn_f32 x_side = b.x - a.x; - Dqn_f32 y_side = b.y - a.y; - Dqn_f32 result = DQN_SQUARED(x_side) + DQN_SQUARED(y_side); + // NOTE: Pythagoras's theorem (a^2 + b^2 = c^2) without the square root + Dqn_f32 a = rhs.x - lhs.x; + Dqn_f32 b = rhs.y - lhs.y; + Dqn_f32 c_squared = DQN_SQUARED(a) + DQN_SQUARED(b); + Dqn_f32 result = c_squared; + return result; +} + +DQN_API Dqn_f32 Dqn_V2_Length_V2x2(Dqn_V2 lhs, Dqn_V2 rhs) +{ + Dqn_f32 result_squared = Dqn_V2_LengthSq_V2x2(lhs, rhs); + Dqn_f32 result = DQN_SQRTF(result_squared); + return result; +} + +DQN_API Dqn_f32 Dqn_V2_LengthSq(Dqn_V2 lhs) +{ + // NOTE: Pythagoras's theorem without the square root + Dqn_f32 c_squared = DQN_SQUARED(lhs.x) + DQN_SQUARED(lhs.y); + Dqn_f32 result = c_squared; + return result; +} + +DQN_API Dqn_f32 Dqn_V2_Length(Dqn_V2 lhs) +{ + Dqn_f32 c_squared = Dqn_V2_LengthSq(lhs); + Dqn_f32 result = DQN_SQRTF(c_squared); return result; } DQN_API Dqn_V2 Dqn_V2_Normalise(Dqn_V2 a) { - Dqn_f32 length_sq = DQN_SQUARED(a.x) + DQN_SQUARED(a.y); - Dqn_f32 length = DQN_SQRTF(length_sq); - Dqn_V2 result = a / length; + Dqn_f32 length = Dqn_V2_Length(a); + Dqn_V2 result = a / length; return result; } DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a) { + // NOTE: Matrix form of a 2D vector can be defined as + // + // x' = x cos(t) - y sin(t) + // y' = x sin(t) + y cos(t) + // + // Calculate a line perpendicular to a vector means rotating the vector by + // 90 degrees + // + // x' = x cos(90) - y sin(90) + // y' = x sin(90) + y cos(90) + // + // Where `cos(90) = 0` and `sin(90) = 1` then, + // + // x' = -y + // y' = +x + Dqn_V2 result = Dqn_V2_InitNx2(-a.y, a.x); return result; } + +DQN_API Dqn_V2 Dqn_V2_Reflect(Dqn_V2 in, Dqn_V2 surface) +{ + Dqn_V2 normal = Dqn_V2_Perpendicular(surface); + Dqn_V2 normal_norm = Dqn_V2_Normalise(normal); + Dqn_f32 signed_dist = Dqn_V2_Dot(in, normal_norm); + Dqn_V2 result = Dqn_V2_InitNx2(in.x, in.y + (-signed_dist * 2.f)); + return result; +} + +DQN_API Dqn_f32 Dqn_V2_Area(Dqn_V2 a) +{ + Dqn_f32 result = a.w * a.h; + return result; +} #endif // !defined(DQN_NO_V2) #if !defined(DQN_NO_V3) @@ -502,6 +621,12 @@ DQN_API Dqn_V3 operator-(Dqn_V3 lhs, Dqn_V3 rhs) return result; } +DQN_API Dqn_V3 operator-(Dqn_V3 lhs) +{ + Dqn_V3 result = Dqn_V3_InitNx3(-lhs.x, -lhs.y, -lhs.z); + return result; +} + DQN_API Dqn_V3 operator+(Dqn_V3 lhs, Dqn_V3 rhs) { Dqn_V3 result = Dqn_V3_InitNx3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); @@ -657,6 +782,12 @@ DQN_API Dqn_V4 operator-(Dqn_V4 lhs, Dqn_V4 rhs) return result; } +DQN_API Dqn_V4 operator-(Dqn_V4 lhs) +{ + Dqn_V4 result = Dqn_V4_InitNx4(-lhs.x, -lhs.y, -lhs.z, -lhs.w); + return result; +} + DQN_API Dqn_V4 operator+(Dqn_V4 lhs, Dqn_V4 rhs) { Dqn_V4 result = Dqn_V4_InitNx4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); @@ -957,16 +1088,16 @@ DQN_API Dqn_M4 Dqn_M4_DivF(Dqn_M4 lhs, Dqn_f32 rhs) return result; } -#if !defined(DQN_NO_FSTRING8) -DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat) +#if !defined(DQN_NO_FSTR8) +DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat) { - Dqn_FString8<256> result = {}; + Dqn_FStr8<256> result = {}; for (int row = 0; row < 4; row++) { for (int it = 0; it < 4; it++) { - if (it == 0) Dqn_FString8_Append(&result, DQN_STRING8("|")); - Dqn_FString8_AppendF(&result, "%.5f", mat.columns[it][row]); - if (it != 3) Dqn_FString8_Append(&result, DQN_STRING8(", ")); - else Dqn_FString8_Append(&result, DQN_STRING8("|\n")); + if (it == 0) Dqn_FStr8_Append(&result, DQN_STR8("|")); + Dqn_FStr8_AppendF(&result, "%.5f", mat.columns[it][row]); + if (it != 3) Dqn_FStr8_Append(&result, DQN_STR8(", ")); + else Dqn_FStr8_Append(&result, DQN_STR8("|\n")); } } @@ -975,6 +1106,94 @@ DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat) #endif #endif // !defined(DQN_M4) +// NOTE: [$M2x3] Dqn_M2x3 ========================================================================== +DQN_API bool operator==(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs) +{ + bool result = DQN_MEMCMP(lhs.e, rhs.e, sizeof(lhs.e[0]) * DQN_ARRAY_UCOUNT(lhs.e)) == 0; + return result; +} + +DQN_API bool operator!=(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs) +{ + bool result = !(lhs == rhs); + return result; +} + +DQN_API Dqn_M2x3 Dqn_M2x3_Identity() +{ + Dqn_M2x3 result = {{ + 1, 0, 0, + 0, 1, 0, + }}; + return result; +} + +DQN_API Dqn_M2x3 Dqn_M2x3_Translate(Dqn_V2 offset) +{ + Dqn_M2x3 result = {{ + 1, 0, offset.x, + 0, 1, offset.y, + }}; + return result; +} + +DQN_API Dqn_M2x3 Dqn_M2x3_Scale(Dqn_V2 scale) +{ + Dqn_M2x3 result = {{ + scale.x, 0, 0, + 0, scale.y, 0, + }}; + return result; +} + +DQN_API Dqn_M2x3 Dqn_M2x3_Rotate(Dqn_f32 radians) +{ + Dqn_M2x3 result = {{ + DQN_COSF(radians), DQN_SINF(radians), 0, + -DQN_SINF(radians), DQN_COSF(radians), 0, + }}; + return result; +} + +DQN_API Dqn_M2x3 Dqn_M2x3_Mul(Dqn_M2x3 m1, Dqn_M2x3 m2) +{ + // NOTE: Ordinarily you can't multiply M2x3 with M2x3 because column count + // (3) != row count (2). We pretend we have two 3x3 matrices with the last + // row set to [0 0 1] and perform a 3x3 matrix multiply. + // + // | (0)a (1)b (2)c | | (0)g (1)h (2)i | + // | (3)d (4)e (5)f | x | (3)j (4)k (5)l | + // | (6)0 (7)0 (8)1 | | (6)0 (7)0 (8)1 | + + Dqn_M2x3 result = {{ + m1.e[0]*m2.e[0] + m1.e[1]*m2.e[3], // a*g + b*j + c*0[omitted], + m1.e[0]*m2.e[1] + m1.e[1]*m2.e[4], // a*h + b*k + c*0[omitted], + m1.e[0]*m2.e[2] + m1.e[1]*m2.e[5] + m1.e[2], // a*i + b*l + c*1, + + m1.e[3]*m2.e[0] + m1.e[4]*m2.e[3], // d*g + e*j + f*0[omitted], + m1.e[3]*m2.e[1] + m1.e[4]*m2.e[4], // d*h + e*k + f*0[omitted], + m1.e[3]*m2.e[2] + m1.e[4]*m2.e[5] + m1.e[5], // d*i + e*l + f*1, + }}; + + return result; +} + +DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2) +{ + // NOTE: Ordinarily you can't multiply M2x3 with V2 because column count (3) + // != row count (2). We pretend we have a V3 with `z` set to `1`. + // + // | (0)a (1)b (2)c | | x | + // | (3)d (4)e (5)f | x | y | + // | 1 | + + Dqn_V2 result = {{ + m1.e[0]*v2.x + m1.e[1]*v2.y + m1.e[2], // a*x + b*y + c*1 + m1.e[3]*v2.x + m1.e[4]*v2.y + m1.e[5], // d*x + e*y + f*1 + }}; + return result; +} + #if !defined(DQN_NO_RECT) // NOTE: [$RECT] Dqn_Rect ========================================================================== DQN_API bool operator==(const Dqn_Rect& lhs, const Dqn_Rect& rhs) @@ -1007,6 +1226,22 @@ DQN_API bool Dqn_Rect_ContainsRect(Dqn_Rect a, Dqn_Rect b) return result; } +DQN_API Dqn_Rect Dqn_Rect_Expand(Dqn_Rect a, Dqn_f32 amount) +{ + Dqn_Rect result = a; + result.pos -= amount; + result.size += (amount * 2.f); + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_ExpandV2(Dqn_Rect a, Dqn_V2 amount) +{ + Dqn_Rect result = a; + result.pos -= amount; + result.size += (amount * 2.f); + return result; +} + DQN_API bool Dqn_Rect_Intersects(Dqn_Rect a, Dqn_Rect b) { Dqn_V2 a_min = a.pos; @@ -1062,6 +1297,12 @@ DQN_API Dqn_RectMinMax Dqn_Rect_MinMax(Dqn_Rect a) return result; } +DQN_API Dqn_f32 Dqn_Rect_Area(Dqn_Rect a) +{ + Dqn_f32 result = a.size.w * a.size.h; + return result; +} + DQN_API Dqn_Rect Dqn_Rect_CutLeftClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip) { Dqn_f32 min_x = rect->pos.x; @@ -1126,8 +1367,71 @@ DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutC return result; } +DQN_API Dqn_V2 Dqn_Rect_InterpolatedPoint(Dqn_Rect rect, Dqn_V2 t01) +{ + Dqn_V2 result = Dqn_V2_InitNx2(rect.pos.w + (rect.size.w * t01.x), + rect.pos.h + (rect.size.h * t01.y)); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_TopLeft(Dqn_Rect rect) +{ + Dqn_V2 result = Dqn_Rect_InterpolatedPoint(rect, Dqn_V2_InitNx2(0, 0)); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_TopRight(Dqn_Rect rect) +{ + Dqn_V2 result = Dqn_Rect_InterpolatedPoint(rect, Dqn_V2_InitNx2(1, 0)); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_BottomLeft(Dqn_Rect rect) +{ + Dqn_V2 result = Dqn_Rect_InterpolatedPoint(rect, Dqn_V2_InitNx2(0, 1)); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_BottomRight(Dqn_Rect rect) +{ + Dqn_V2 result = Dqn_Rect_InterpolatedPoint(rect, Dqn_V2_InitNx2(1, 1)); + return result; +} #endif // !defined(DQN_NO_RECT) +// NOTE: [$MATH] Raycast =========================================================================== + +DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b) +{ + // NOTE: Parametric equation of a line + // + // p = o + (t*d) + // + // - o is the starting 2d point + // - d is the direction of the line + // - t is a scalar that scales along the direction of the point + // + // To determine if a ray intersections a ray, we want to solve + // + // (o_a + (t_a * d_a)) = (o_b + (t_b * d_b)) + // + // Where '_a' and '_b' represent the 1st and 2nd point's origin, direction + // and 't' components respectively. This is 2 equations with 2 unknowns + // (`t_a` and `t_b`) which we can solve for by expressing the equation in + // terms of `t_a` and `t_b`. + // + // Working that math out produces the formula below for 't'. + + Dqn_RaycastLineIntersectV2Result result = {}; + Dqn_f32 denominator = ((dir_b.y * dir_a.x) - (dir_b.x * dir_a.y)); + if (denominator != 0.0f) { + result.t_a = (((origin_a.y - origin_b.y) * dir_b.x) + ((origin_b.x - origin_a.x) * dir_b.y)) / denominator; + result.t_b = (((origin_a.y - origin_b.y) * dir_a.x) + ((origin_b.x - origin_a.x) * dir_a.y)) / denominator; + result.hit = true; + } + return result; +} + // NOTE: [$MATH] Other ============================================================================= DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b) { diff --git a/dqn_math.h b/dqn_math.h index b4e000e..61a5a95 100644 --- a/dqn_math.h +++ b/dqn_math.h @@ -10,8 +10,9 @@ union Dqn_V2I int32_t data[2]; }; -#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}} -#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}} +#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}} +#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}} +#define Dqn_V2I_InitV2(xy) DQN_LITERAL(Dqn_V2I){{(int32_t)(xy).x, (int32_t)(xy).y}} DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs); DQN_API bool operator==(Dqn_V2I lhs, Dqn_V2I rhs); @@ -20,6 +21,7 @@ DQN_API bool operator<=(Dqn_V2I lhs, Dqn_V2I rhs); DQN_API bool operator< (Dqn_V2I lhs, Dqn_V2I rhs); DQN_API bool operator> (Dqn_V2I lhs, Dqn_V2I rhs); DQN_API Dqn_V2I operator- (Dqn_V2I lhs, Dqn_V2I rhs); +DQN_API Dqn_V2I operator- (Dqn_V2I lhs); DQN_API Dqn_V2I operator+ (Dqn_V2I lhs, Dqn_V2I rhs); DQN_API Dqn_V2I operator* (Dqn_V2I lhs, Dqn_V2I rhs); DQN_API Dqn_V2I operator* (Dqn_V2I lhs, Dqn_f32 rhs); @@ -76,8 +78,11 @@ union Dqn_V2 Dqn_f32 data[2]; }; -#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}} -#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(y)}} +#define Dqn_V2_Zero DQN_LITERAL(Dqn_V2){{(Dqn_f32)(0), (Dqn_f32)(0)}} +#define Dqn_V2_One DQN_LITERAL(Dqn_V2){{(Dqn_f32)(1), (Dqn_f32)(1)}} +#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}} +#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(y)}} +#define Dqn_V2_InitV2I(xy) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(xy).x, (Dqn_f32)(xy).y}} DQN_API bool operator!=(Dqn_V2 lhs, Dqn_V2 rhs); DQN_API bool operator==(Dqn_V2 lhs, Dqn_V2 rhs); @@ -87,6 +92,7 @@ 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_f32 rhs); +DQN_API Dqn_V2 operator- (Dqn_V2 lhs); 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); @@ -102,16 +108,23 @@ 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, int32_t 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_f32 rhs); -DQN_API Dqn_V2I Dqn_V2_ToV2I(Dqn_V2 a); -DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Max(Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a); -DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_f32 Dqn_V2_LengthSq(Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Normalise(Dqn_V2 a); +DQN_API Dqn_V2I Dqn_V2_ToV2I (Dqn_V2 a); +DQN_API Dqn_V2 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a); +DQN_API Dqn_f32 Dqn_V2_Dot (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_f32 Dqn_V2_LengthSq_V2x2(Dqn_V2 lhs, Dqn_V2 rhs); +DQN_API Dqn_f32 Dqn_V2_Length_V2x2 (Dqn_V2 lhs, Dqn_V2 rhs); +DQN_API Dqn_f32 Dqn_V2_LengthSq (Dqn_V2 lhs); +DQN_API Dqn_f32 Dqn_V2_Length (Dqn_V2 lhs); +DQN_API Dqn_V2 Dqn_V2_Normalise (Dqn_V2 a); DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a); +DQN_API Dqn_V2 Dqn_V2_Reflect (Dqn_V2 in, Dqn_V2 surface); +DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a); #endif // !defined(DQN_NO_V2) #if !defined(DQN_NO_V3) @@ -134,6 +147,7 @@ DQN_API bool operator<=(Dqn_V3 lhs, Dqn_V3 rhs); DQN_API bool operator< (Dqn_V3 lhs, Dqn_V3 rhs); DQN_API bool operator> (Dqn_V3 lhs, Dqn_V3 rhs); DQN_API Dqn_V3 operator- (Dqn_V3 lhs, Dqn_V3 rhs); +DQN_API Dqn_V3 operator- (Dqn_V3 lhs); DQN_API Dqn_V3 operator+ (Dqn_V3 lhs, Dqn_V3 rhs); DQN_API Dqn_V3 operator* (Dqn_V3 lhs, Dqn_V3 rhs); DQN_API Dqn_V3 operator* (Dqn_V3 lhs, Dqn_f32 rhs); @@ -163,30 +177,33 @@ union Dqn_V4 struct { Dqn_f32 r, g, b, a; }; #if !defined(DQN_NO_V3) Dqn_V3 rgb; + Dqn_V3 xyz; #endif Dqn_f32 data[4]; }; -#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}} -#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}} +#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}} +#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}} +#define Dqn_V4_Init_V3x1_Nx1(xyz, w) DQN_LITERAL(Dqn_V4){{xyz.x, xyz.y, xyz.z, w}} -bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs); -bool operator==(Dqn_V4 lhs, Dqn_V4 rhs); -bool operator>=(Dqn_V4 lhs, Dqn_V4 rhs); -bool operator<=(Dqn_V4 lhs, Dqn_V4 rhs); -bool operator< (Dqn_V4 lhs, Dqn_V4 rhs); -bool 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); -Dqn_V4 operator* (Dqn_V4 lhs, Dqn_f32 rhs); -Dqn_V4 operator* (Dqn_V4 lhs, int32_t rhs); -Dqn_V4 operator/ (Dqn_V4 lhs, Dqn_f32 rhs); -Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_V4 rhs); -Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_f32 rhs); -Dqn_V4 &operator*=(Dqn_V4 &lhs, int32_t rhs); -Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 rhs); -Dqn_V4 &operator+=(Dqn_V4 &lhs, Dqn_V4 rhs); +DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API bool operator==(Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API bool operator>=(Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API bool operator<=(Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API bool operator< (Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API bool operator> (Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 operator- (Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 operator- (Dqn_V4 lhs); +DQN_API Dqn_V4 operator+ (Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 operator* (Dqn_V4 lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 operator* (Dqn_V4 lhs, Dqn_f32 rhs); +DQN_API Dqn_V4 operator* (Dqn_V4 lhs, int32_t rhs); +DQN_API Dqn_V4 operator/ (Dqn_V4 lhs, Dqn_f32 rhs); +DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_f32 rhs); +DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, int32_t rhs); +DQN_API Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 rhs); +DQN_API Dqn_V4 &operator+=(Dqn_V4 &lhs, Dqn_V4 rhs); #endif // !defined(DQN_NO_V4) #if !defined(DQN_NO_M4) @@ -217,11 +234,26 @@ DQN_API Dqn_M4 Dqn_M4_SubF(Dqn_M4 lhs, Dqn_f32 rhs); DQN_API Dqn_M4 Dqn_M4_MulF(Dqn_M4 lhs, Dqn_f32 rhs); DQN_API Dqn_M4 Dqn_M4_DivF(Dqn_M4 lhs, Dqn_f32 rhs); -#if !defined(DQN_NO_FSTRING8) -DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat); +#if !defined(DQN_NO_FSTR8) +DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat); #endif #endif // !defined(DQN_M4) +union Dqn_M2x3 +{ + Dqn_f32 e[6]; + Dqn_f32 row[2][3]; +}; + +DQN_API bool operator==(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs); +DQN_API bool operator!=(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs); +DQN_API Dqn_M2x3 Dqn_M2x3_Identity (); +DQN_API Dqn_M2x3 Dqn_M2x3_Translate(Dqn_V2 offset); +DQN_API Dqn_M2x3 Dqn_M2x3_Scale (Dqn_V2 scale); +DQN_API Dqn_M2x3 Dqn_M2x3_Rotate (Dqn_f32 radians); +DQN_API Dqn_M2x3 Dqn_M2x3_Mul (Dqn_M2x3 m1, Dqn_M2x3 m2); +DQN_API Dqn_V2 Dqn_M2x3_MulV2 (Dqn_M2x3 m1, Dqn_V2 v2); + // NOTE: [$RECT] Dqn_Rect ========================================================================== #if !defined(DQN_NO_RECT) #if defined(DQN_NO_V2) @@ -241,14 +273,22 @@ struct Dqn_RectMinMax #define Dqn_Rect_InitV2x2(pos, size) DQN_LITERAL(Dqn_Rect){(pos), (size)} #define Dqn_Rect_InitNx4(pos_x, pos_y, size_w, size_h) DQN_LITERAL(Dqn_Rect){DQN_LITERAL(Dqn_V2){{pos_x, pos_y}}, DQN_LITERAL(Dqn_V2){{size_w, size_h}}} -DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs); -DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect); -DQN_API bool Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p); -DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b); -DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b); -DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b); -DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b); -DQN_API Dqn_RectMinMax Dqn_Rect_MinMax (Dqn_Rect a); +DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs); +DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect); +DQN_API bool Dqn_Rect_ContainsPoint (Dqn_Rect rect, Dqn_V2 p); +DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_Expand (Dqn_Rect a, Dqn_f32 amount); +DQN_API Dqn_Rect Dqn_Rect_ExpandV2 (Dqn_Rect a, Dqn_V2 amount); +DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_RectMinMax Dqn_Rect_MinMax (Dqn_Rect a); +DQN_API Dqn_f32 Dqn_Rect_Area (Dqn_Rect a); +DQN_API Dqn_V2 Dqn_Rect_InterpolatedPoint(Dqn_Rect rect, Dqn_V2 t01); +DQN_API Dqn_V2 Dqn_Rect_TopLeft (Dqn_Rect rect); +DQN_API Dqn_V2 Dqn_Rect_TopRight (Dqn_Rect rect); +DQN_API Dqn_V2 Dqn_Rect_BottomLeft (Dqn_Rect rect); +DQN_API Dqn_V2 Dqn_Rect_BottomRight (Dqn_Rect rect); enum Dqn_RectCutClip { @@ -294,6 +334,26 @@ struct Dqn_RectCut DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip); #endif // !defined(DQN_NO_RECT) +// NOTE: [$MATH] Raycast =========================================================================== +// +// NOTE: API +// @proc Dqn_Raycast_LineIntersectV2 +// @desc Calculate the intersection point of 2 rays returning a `t` value +// which is how much along the direction of the 'ray' did the intersection +// occur. +// +// The arguments passed in do not need to be normalised for the function to +// work. + +struct Dqn_RaycastLineIntersectV2Result +{ + bool hit; // True if there was an intersection, false if the lines are parallel + Dqn_f32 t_a; // Distance along `dir_a` that the intersection occurred, e.g. `origin_a + (dir_a * t_a)` + Dqn_f32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)` +}; + +DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b); + // NOTE: [$MATH] Other ============================================================================= DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); diff --git a/dqn_memory.cpp b/dqn_memory.cpp index 142c6fd..92d4848 100644 --- a/dqn_memory.cpp +++ b/dqn_memory.cpp @@ -1,3 +1,4 @@ +#if !defined(DQN_PLATFORM_EMSCRIPTEN) // NOTE: [$VMEM] Dqn_VMem ========================================================================== DQN_FILE_SCOPE uint32_t Dqn_VMem_ConvertPageToOSFlags_(uint32_t protect) { @@ -115,9 +116,9 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags) if (!ptr || size == 0) return 0; - static Dqn_String8 const ALIGNMENT_ERROR_MSG = - DQN_STRING8("Page protection requires pointers to be page aligned because we " - "can only guard memory at a multiple of the page boundary."); + static Dqn_Str8 const ALIGNMENT_ERROR_MSG = + DQN_STR8("Page protection requires pointers to be page aligned because we " + "can only guard memory at a multiple of the page boundary."); DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data); DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data); @@ -132,7 +133,7 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags) #else Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_WinError error = Dqn_Win_LastError(scratch.arena); - DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, DQN_STRING_FMT(error.msg)); + DQN_ASSERTF(result, "VirtualProtect failed (%u): %.*s", error.code, DQN_STR_FMT(error.msg)); #endif } #else @@ -142,6 +143,48 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags) return result; } +#endif // !defined(DQN_PLATFORM_EMSCRIPTEN) + +// NOTE: [$MEMF] Dqn_MemAPI ================================================================== +#if !defined(DQN_PLATFORM_EMSCRIPTEN) +DQN_API Dqn_MemAPI Dqn_MemAPI_InitOSVirtual() +{ + Dqn_MemAPI result = {}; + result.reserve = Dqn_VMem_Reserve; + result.commit = Dqn_VMem_Commit; + result.release = Dqn_VMem_Release; + return result; +} +#endif // !defined(DQN_PLATFORM_EMSCRIPTEN) + +void *Dqn_MemAPI_CRTReserve(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags) +{ + (void)page_flags; + (void)commit; + void *result = calloc(1, size); + return result; +} + +bool Dqn_MemAPI_CRTCommit(void *ptr, Dqn_usize size, uint32_t page_flags) +{ + (void)ptr; (void)size; (void)page_flags; + return true; +} + +void Dqn_MemAPI_CRTRelease(void *ptr, Dqn_usize size) +{ + (void)size; + free(ptr); +} + +DQN_API Dqn_MemAPI Dqn_MemAPI_InitCRT() +{ + Dqn_MemAPI result = {}; + result.reserve = Dqn_MemAPI_CRTReserve; + result.commit = Dqn_MemAPI_CRTCommit; + result.release = Dqn_MemAPI_CRTRelease; + return result; +} // NOTE: [$MEMB] Dqn_MemBlock ====================================================================== DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock const *block, Dqn_usize size, uint8_t alignment, uint32_t flags) @@ -176,7 +219,7 @@ DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock co return result; } -Dqn_usize Dqn_MemBlock_MetadataSize() +DQN_API Dqn_usize Dqn_MemBlock_MetadataSize() { Dqn_usize init_poison_page = DQN_ASAN_POISON ? DQN_ASAN_POISON_GUARD_SIZE : 0; Dqn_usize alignment = DQN_ASAN_POISON ? DQN_ASAN_POISON_ALIGNMENT : alignof(Dqn_MemBlock); @@ -184,7 +227,7 @@ Dqn_usize Dqn_MemBlock_MetadataSize() return result; } -DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uint32_t flags) +DQN_API Dqn_MemBlock *Dqn_MemBlock_InitMemAPI(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_api) { DQN_ASSERTF(g_dqn_library->os_page_size, "Library needs to be initialised by calling Dqn_Library_Init()"); DQN_ASSERTF(Dqn_IsPowerOfTwo(g_dqn_library->os_page_size), "Invalid page size"); @@ -200,16 +243,17 @@ DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uin // NOTE: Avoid 1 syscall by committing on reserve if amounts are equal Dqn_VMemCommit commit_on_reserve = commit_aligned == reserve_aligned ? Dqn_VMemCommit_Yes : Dqn_VMemCommit_No; - auto *result = DQN_CAST(Dqn_MemBlock *)Dqn_VMem_Reserve(reserve_aligned, commit_on_reserve, Dqn_VMemPage_ReadWrite); + auto *result = DQN_CAST(Dqn_MemBlock *)mem_api.reserve(reserve_aligned, commit_on_reserve, Dqn_VMemPage_ReadWrite); if (result) { // NOTE: Commit pages if we did not commit on the initial range. if (!commit_on_reserve) - Dqn_VMem_Commit(result, commit_aligned, Dqn_VMemPage_ReadWrite); + mem_api.commit(result, commit_aligned, Dqn_VMemPage_ReadWrite); - result->data = DQN_CAST(uint8_t *)result + metadata_size; - result->size = reserve_aligned - metadata_size; - result->commit = commit_aligned - metadata_size; - result->flags = DQN_CAST(uint8_t)flags; + result->mem_api = mem_api; + result->data = DQN_CAST(uint8_t *)result + metadata_size; + result->size = reserve_aligned - metadata_size; + result->commit = commit_aligned - metadata_size; + result->flags = DQN_CAST(uint8_t)flags; // NOTE: Poison (guard page + commit). We do *not* poison the entire // block, only the commit pages. Since we may reserve large amounts of @@ -226,6 +270,12 @@ DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uin return result; } +DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_api) +{ + Dqn_MemBlock *result = Dqn_MemBlock_InitMemAPI(reserve, commit, flags, mem_api); + return result; +} + DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem) { DQN_ASSERT(zero_mem == Dqn_ZeroMem_Yes || zero_mem == Dqn_ZeroMem_No); @@ -239,8 +289,9 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al if (new_used > block->size) return result; - result = DQN_CAST(char *)block->data + size_required.data_offset; - block->used = new_used; + result = DQN_CAST(char *)block->data + size_required.data_offset; + block->used = new_used; + block->used_hwm = DQN_MAX(block->used_hwm, new_used); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment)); if (DQN_ASAN_POISON) @@ -255,7 +306,7 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al Dqn_usize commit_size = Dqn_AlignUpPowerOfTwo(block->used - block->commit, g_dqn_library->os_page_size); void *commit_ptr = (void *)Dqn_AlignUpPowerOfTwo((char *)block->data + block->commit, g_dqn_library->os_page_size); block->commit += commit_size; - Dqn_VMem_Commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite); + block->mem_api.commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite); DQN_ASSERT(block->commit <= block->size); if (DQN_ASAN_POISON) { // NOTE: Poison newly committed pages that aren't being used. @@ -275,7 +326,7 @@ DQN_API void Dqn_MemBlock_Free(Dqn_MemBlock *block) Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize(); if (DQN_ASAN_POISON) Dqn_ASAN_UnpoisonMemoryRegion(block, release_size); - Dqn_VMem_Release(block, release_size); + block->mem_api.release(block, release_size); } DQN_API void Dqn_MemBlock_Pop(Dqn_MemBlock *block, Dqn_usize size) @@ -339,7 +390,15 @@ DQN_API Dqn_MemBlock *Dqn_Arena_Grow(Dqn_Arena *arena, Dqn_usize reserve, Dqn_us if (arena->allocs_are_allowed_to_leak) mem_block_flags |= Dqn_MemBlockFlag_AllocRecordLeakPermitted; - Dqn_MemBlock *result = Dqn_MemBlock_Init(reserve, commit, mem_block_flags); + if (!arena->mem_api.reserve) { + #if defined(DQN_PLATFORM_EMSCRIPTEN) + arena->mem_api = Dqn_MemAPI_InitCRT(); + #else + arena->mem_api = Dqn_MemAPI_InitOSVirtual(); + #endif + } + + Dqn_MemBlock *result = Dqn_MemBlock_Init(reserve, commit, mem_block_flags, arena->mem_api); if (result) { if (!arena->head) arena->head = result; @@ -475,29 +534,19 @@ Dqn_ArenaTempMemoryScope::~Dqn_ArenaTempMemoryScope() Dqn_Arena_EndTempMemory(temp_memory, cancel); } -DQN_API Dqn_ArenaStatString Dqn_Arena_StatString(Dqn_ArenaStat const *stat) +DQN_API Dqn_ArenaInfo Dqn_Arena_Info(Dqn_Arena const *arena) { - // NOTE: We use a non-standard format string that is only usable via - // stb sprintf that GCC warns about as an error. This pragma mutes that. - DQN_GCC_WARNING_PUSH - DQN_GCC_WARNING_DISABLE("-Wformat-invalid-specifier") - DQN_GCC_WARNING_DISABLE("-Wformat-extra-args") - Dqn_ArenaStatString result = {}; - int size16 = STB_SPRINTF_DECORATE(snprintf)(result.data, DQN_ARRAY_ICOUNT(result.data), - "ArenaStat{" - "used/hwm=%_$$zd/%_$$zd, " - "cap/hwm=%_$$zd/%_$$zd, " - "wasted/hwm=%_$$zd/%_$$zd, " - "blocks/hwm=%_$$u/%_$$u, " - "syscalls=%'zd" - "}", - stat->used, stat->used_hwm, - stat->capacity, stat->capacity_hwm, - stat->wasted, stat->wasted_hwm, - stat->blocks, stat->blocks_hwm, - stat->syscalls); - result.size = Dqn_Safe_SaturateCastIntToU16(size16); - DQN_GCC_WARNING_POP + Dqn_ArenaInfo result = {}; + if (!arena) + return result; + + for (Dqn_MemBlock const *block = arena->head; block; block = block->next) { + result.capacity += block->size; + result.used += block->used; + result.used_hwm += block->used_hwm; + result.commit += block->commit; + result.wasted += block->next ? (block->size - block->used) : 0; + } return result; } @@ -541,14 +590,14 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc(Dqn_ArenaCatalog *catalog, Dqn_usize b return result; } -DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args) +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, va_list args) { Dqn_Arena *result = Dqn_ArenaCatalog_Alloc(catalog, byte_size, commit); - result->label = Dqn_String8_InitFV(Dqn_Arena_Allocator(result), fmt, args); + result->label = Dqn_Str8_InitFV(Dqn_Arena_Allocator(result), fmt, args); return result; } -DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...) +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); diff --git a/dqn_memory.h b/dqn_memory.h index a86cd81..3e8354f 100644 --- a/dqn_memory.h +++ b/dqn_memory.h @@ -33,11 +33,32 @@ enum Dqn_VMemPage Dqn_VMemPage_AllocRecordLeakPermitted = 1 << 2, }; +#if !defined(DQN_PLATFORM_EMSCRIPTEN) DQN_API void *Dqn_VMem_Reserve (Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags); 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_Release (void *ptr, Dqn_usize size); DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags); +#endif + +// NOTE: [$MEMF] Dqn_MemAPI ================================================================== +// Interface for specifying the routines for memory allocation for Dqn_MemBlock + +typedef void *(Dqn_MemReserveFunc)(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags); +typedef bool (Dqn_MemCommitFunc) (void *ptr, Dqn_usize size, uint32_t page_flags); +typedef void (Dqn_MemReleaseFunc)(void *ptr, Dqn_usize size); + +struct Dqn_MemAPI +{ + Dqn_MemReserveFunc *reserve; + Dqn_MemCommitFunc *commit; + Dqn_MemReleaseFunc *release; +}; + +#if !defined(DQN_PLATFORM_EMSCRIPTEN) +DQN_API Dqn_MemAPI Dqn_MemAPI_InitOSVirtual(); +#endif +DQN_API Dqn_MemAPI Dqn_MemAPI_InitCRT(); // NOTE: [$MEMB] Dqn_MemBlock ====================================================================== // Encapsulates allocation of objects from a raw block of memory by bumping a @@ -103,8 +124,10 @@ enum Dqn_MemBlockFlag struct Dqn_MemBlock { + Dqn_MemAPI mem_api; void *data; Dqn_usize used; + Dqn_usize used_hwm; Dqn_usize size; Dqn_usize commit; Dqn_MemBlock *next; @@ -130,13 +153,14 @@ struct Dqn_MemBlockSizeRequiredResult Dqn_usize block_size; }; -DQN_API Dqn_usize Dqn_MemBlock_MetadataSize(uint8_t flags); -DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock const *block, Dqn_usize size, uint8_t alignment, uint32_t flags); -DQN_API Dqn_MemBlock * Dqn_MemBlock_Init (Dqn_usize reserve, Dqn_usize commit, uint32_t flags); -DQN_API void * Dqn_MemBlock_Alloc (Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem); -DQN_API void Dqn_MemBlock_Free (Dqn_MemBlock *block); -DQN_API void Dqn_MemBlock_Pop (Dqn_MemBlock *block, Dqn_usize size); -DQN_API void Dqn_MemBlock_PopTo (Dqn_MemBlock *block, Dqn_usize to); +DQN_API Dqn_usize Dqn_MemBlock_MetadataSize (uint8_t flags); +DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired (Dqn_MemBlock const *block, Dqn_usize size, uint8_t alignment, uint32_t flags); +DQN_API Dqn_MemBlock * Dqn_MemBlock_InitMemAPI(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_functions); +DQN_API Dqn_MemBlock * Dqn_MemBlock_Init (Dqn_usize reserve, Dqn_usize commit, uint32_t flags); +DQN_API void * Dqn_MemBlock_Alloc (Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem); +DQN_API void Dqn_MemBlock_Free (Dqn_MemBlock *block); +DQN_API void Dqn_MemBlock_Pop (Dqn_MemBlock *block, Dqn_usize size); +DQN_API void Dqn_MemBlock_PopTo (Dqn_MemBlock *block, Dqn_usize to); #define Dqn_MemBlock_New(block, Type, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type), alignof(Type), zero_mem) #define Dqn_MemBlock_NewArray(block, Type, count, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type) * count, alignof(Type), zero_mem) @@ -205,27 +229,13 @@ DQN_API void Dqn_MemBlock_PopTo (Dqn_MemBlock *b // @proc Dqn_Arena_EndTempMemory // @desc End an allocation scope previously begun by calling begin scope. - -// @proc Dqn_Arena_StatString -// @desc Dump the stats of the given arena to a string -// @param[in] arena The arena to dump stats for -// @return A stack allocated string containing the stats of the arena - -// @proc Dqn_Arena_LogStats -// @desc Dump the stats of the given arena to the memory log-stream. -// @param[in] arena The arena to dump stats for -struct Dqn_ArenaStat +struct Dqn_ArenaInfo { - Dqn_usize capacity; // Total allocating capacity of the arena in bytes - Dqn_usize used; // Total amount of bytes used in the arena - Dqn_usize wasted; // Orphaned space in blocks due to allocations requiring more space than available in the active block - uint32_t blocks; // Number of memory blocks in the arena - Dqn_usize syscalls; // Number of memory allocation syscalls into the OS - - Dqn_usize capacity_hwm; // High-water mark for 'capacity' - Dqn_usize used_hwm; // High-water mark for 'used' - Dqn_usize wasted_hwm; // High-water mark for 'wasted' - uint32_t blocks_hwm; // High-water mark for 'blocks' + Dqn_usize capacity; // Total allocating capacity of the arena in bytes + Dqn_usize used; // Total amount of bytes used in the arena + Dqn_usize commit; // Total amount of bytes committed in the arena + Dqn_usize wasted; // Orphaned space in blocks due to allocations requiring more space than available in the active block + Dqn_usize used_hwm; // High-water mark for 'used' }; struct Dqn_ArenaBlock @@ -234,7 +244,7 @@ struct Dqn_ArenaBlock void *memory; // Backing memory of the block Dqn_usize size; // Size of the block Dqn_usize used; // Number of bytes used up in the block. Always less than the commit amount. - Dqn_usize hwm_used;// High-water mark for 'used' bytes in this block + Dqn_usize used_hwm;// High-water mark for 'used' bytes in this block Dqn_usize commit; // Number of bytes in the block physically backed by pages Dqn_ArenaBlock *prev; // Previous linked block Dqn_ArenaBlock *next; // Next linked block @@ -249,12 +259,13 @@ struct Dqn_ArenaStatString struct Dqn_Arena { - bool allocs_are_allowed_to_leak; - Dqn_String8 label; // Optional label to describe the arena - Dqn_MemBlock *head; // Active block the arena is allocating from - Dqn_MemBlock *curr; // Active block the arena is allocating from - Dqn_MemBlock *tail; // Last block in the linked list of blocks - uint64_t blocks; + Dqn_MemAPI mem_api; + bool allocs_are_allowed_to_leak; + Dqn_Str8 label; // Optional label to describe the arena + Dqn_MemBlock *head; // Active block the arena is allocating from + Dqn_MemBlock *curr; // Active block the arena is allocating from + Dqn_MemBlock *tail; // Last block in the linked list of blocks + uint64_t blocks; }; struct Dqn_ArenaTempMemory @@ -305,9 +316,8 @@ DQN_API void Dqn_Arena_Free (Dqn_Arena *arena, Dqn_Zero DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena); DQN_API void Dqn_Arena_EndTempMemory (Dqn_ArenaTempMemory temp_memory, bool cancel); -// NOTE: Arena Stats =============================================================================== -DQN_API Dqn_ArenaStatString Dqn_Arena_StatString (Dqn_ArenaStat const *stat); -DQN_API void Dqn_Arena_LogStats (Dqn_Arena const *arena); +// NOTE: Arena Info =============================================================================== +DQN_API Dqn_ArenaInfo Dqn_Arena_Info (Dqn_Arena const *arena); // NOTE: [$ACAT] Dqn_ArenaCatalog ================================================================== struct Dqn_ArenaCatalogItem @@ -328,5 +338,5 @@ struct Dqn_ArenaCatalog DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena); DQN_API void Dqn_ArenaCatalog_Add (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena); DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit); -DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args); -DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...); +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, ...); diff --git a/dqn_os.cpp b/dqn_os.cpp new file mode 100644 index 0000000..d81f636 --- /dev/null +++ b/dqn_os.cpp @@ -0,0 +1,136 @@ +DQN_API void Dqn_OS_Exit(uint32_t exit_code) +{ + #if defined(DQN_OS_WIN32) + ExitProcess(exit_code); + #else + exit(exit_code); + #endif +} + +// NOTE: [$EXEC] Dqn_OSExec ======================================================================== +DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle) +{ + Dqn_OSExecResult result = {}; + if (!handle.process || handle.os_error_code) { + result.os_error_code = handle.os_error_code; + return result; + } + + #if defined(DQN_OS_WIN32) + DWORD exec_result = WaitForSingleObject(handle.process, INFINITE); + if (exec_result == WAIT_FAILED) { + result.os_error_code = GetLastError(); + return result; + } + + DWORD exit_status; + if (!GetExitCodeProcess(handle.process, &exit_status)) { + result.os_error_code = GetLastError(); + return result; + } + + result.exit_code = exit_status; + CloseHandle(handle.process); + #elif defined(DQN_PLATFORM_EMSCRIPTEN) + DQN_ASSERTF(false, "Unsupported operation"); + #else + for (;;) { + int status = 0; + if (waitpid(DQN_CAST(pid_t)handle.process, &status, 0) < 0) { + result.os_error_code = errno; + break; + } + + if (WIFEXITED(status)) { + result.exit_code = WEXITSTATUS(status); + break; + } + + if (WIFSIGNALLED(status)) { + result.os_error_code = WTERMSIG(status); + break; + } + } + #endif + return result; +} + +DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Str8 cmd, Dqn_Str8 working_dir) +{ + Dqn_OSExecAsyncHandle result = {}; + if (cmd.size == 0) + return result; + + #if defined(DQN_OS_WIN32) + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd); + Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir); + + PROCESS_INFORMATION proc_info = {}; + STARTUPINFOW startup_info = {}; + startup_info.cb = sizeof(STARTUPINFOW); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info); + if (!create_result) { + result.os_error_code = GetLastError(); + return result; + } + + CloseHandle(proc_info.hThread); + result.process = proc_info.hProcess; + #else + DQN_ASSERTF(false, "Unsupported operation"); + // TODO: This API will need to switch to an array of strings for unix + #if 0 + pid_t child_pid = fork(); + if (child_pid < 0) { + result.os_error_code = errno; + return result; + } + + if (child_pid == 0) { + if (working_dir.size) { + if (chdir(working_dir.data) == -1) { + result.os_error_code = errno; + return result; + } + } + + if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { + result.os_error_code = errno; + return result; + } + DQN_INVALID_CODE_PATH; + } + + result.process = DQN_CAST(void *)child_pid; + #endif + #endif + return result; +} + +DQN_API Dqn_OSExecResult Dqn_OS_Exec(Dqn_Str8 cmd, Dqn_Str8 working_dir) +{ + Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd, working_dir); + Dqn_OSExecResult result = Dqn_OS_ExecWait(async_handle); + return result; +} + +DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir) +{ + Dqn_OSExecResult result = Dqn_OS_Exec(cmd, working_dir); + if (result.os_error_code || result.exit_code) { + if (result.os_error_code) { + Dqn_Log_ErrorF("OS failed to execute the requested command returning the error code %u. The command was\n\n%.*s", result.os_error_code, DQN_STR_FMT(cmd)); + Dqn_OS_Exit(result.os_error_code); + } + + if (result.exit_code) { + Dqn_Log_ErrorF("OS executed command and returned a non-zero status: %u. The command was\n\n%.*s", result.exit_code, DQN_STR_FMT(cmd)); + Dqn_OS_Exit(result.exit_code); + } + } +} diff --git a/dqn_os.h b/dqn_os.h new file mode 100644 index 0000000..c0e0455 --- /dev/null +++ b/dqn_os.h @@ -0,0 +1,18 @@ +// NOTE: [$EXEC] Dqn_OSExec ======================================================================== +struct Dqn_OSExecAsyncHandle +{ + uint32_t os_error_code; + void *process; +}; + +struct Dqn_OSExecResult +{ + uint32_t os_error_code; + uint32_t exit_code; +}; + +DQN_API void Dqn_OS_Exit (uint32_t exit_code); +DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle); +DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Str8 cmd, Dqn_Str8 working_dir); +DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Str8 cmd, Dqn_Str8 working_dir); +DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir); diff --git a/dqn_platform.cpp b/dqn_platform.cpp index 4dd6dc6..51770fa 100644 --- a/dqn_platform.cpp +++ b/dqn_platform.cpp @@ -19,15 +19,15 @@ DQN_API uint64_t Dqn_Win__FileTimeToSeconds(FILETIME const *time) } #endif -DQN_API bool Dqn_Fs_Exists(Dqn_String8 path) +DQN_API bool Dqn_Fs_Exists(Dqn_Str8 path) { bool result = false; - if (!Dqn_String8_IsValid(path)) + if (!Dqn_Str8_IsValid(path)) return result; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { @@ -35,26 +35,23 @@ DQN_API bool Dqn_Fs_Exists(Dqn_String8 path) !(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } } - #elif defined(DQN_OS_UNIX) + #else struct stat stat_result; if (lstat(path.data, &stat_result) != -1) result = S_ISREG(stat_result.st_mode) || S_ISLNK(stat_result.st_mode); - #else - #error Unimplemented #endif - return result; } -DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path) +DQN_API bool Dqn_Fs_DirExists(Dqn_Str8 path) { bool result = false; - if (!Dqn_String8_IsValid(path)) + if (!Dqn_Str8_IsValid(path)) return result; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) { @@ -63,25 +60,23 @@ DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path) } } - #elif defined(DQN_OS_UNIX) + #else struct stat stat_result; if (lstat(path.data, &stat_result) != -1) result = S_ISDIR(stat_result.st_mode); - #else - #error Unimplemented #endif return result; } -DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path) +DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_Str8 path) { Dqn_FsInfo result = {}; - if (!Dqn_String8_IsValid(path)) + if (!Dqn_Str8_IsValid(path)) return result; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) @@ -105,7 +100,7 @@ DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path) } } - #elif defined(DQN_OS_UNIX) + #else struct stat file_stat; if (lstat(path.data, &file_stat) != -1) { result.exists = true; @@ -117,21 +112,17 @@ DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path) // shoddily deal with this. result.create_time_in_s = DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s); } - - #else - #error Unimplemented #endif - return result; } -DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite) +DQN_API bool Dqn_Fs_Copy(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite) { bool result = false; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src); - Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest); + Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); + Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); int fail_if_exists = overwrite == false; result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; @@ -139,12 +130,13 @@ DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite) if (!result) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_ErrorF("Failed to copy the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s", - DQN_STRING_FMT(src), - DQN_STRING_FMT(dest), - DQN_STRING_FMT(error.msg)); + DQN_STR_FMT(src), + DQN_STR_FMT(dest), + DQN_STR_FMT(error.msg)); } - - #elif defined(DQN_OS_UNIX) + #elif defined(DQN_PLATFORM_EMSCRIPTEN) + DQN_ASSERTF(false, "Unsupported on Emscripten because of their VFS model"); + #else int src_fd = open(src.data, O_RDONLY); int dest_fd = open(dest.data, O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : 0)); @@ -160,20 +152,18 @@ DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite) if (dest_fd != -1) close(dest_fd); - #else - #error Unimplemented #endif return result; } -DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path) +DQN_API bool Dqn_Fs_MakeDir(Dqn_Str8 path) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); bool result = true; #if defined(DQN_OS_WIN32) - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); // NOTE: Go back from the end of the string to all the directories in the // string, and try to create them. Since Win32 API cannot create @@ -217,15 +207,13 @@ DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path) path16.data[index] = temp; // Undo null termination } } - - #elif defined(DQN_OS_UNIX) - + #else // 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_Str8 copy = Dqn_Str8_Copy(scratch.allocator, path); for (Dqn_usize index = copy.size - 1; index < copy.size; index--) { bool first_char = index == (copy.size - 1); char ch = copy.data[index]; @@ -268,22 +256,19 @@ DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path) result |= mkdir(copy.data, 0774) == 0; if (index != 0) copy.data[path_index] = temp; } - - #else - #error Unimplemented #endif return result; } -DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) +DQN_API bool Dqn_Fs_Move(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite) { bool result = false; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 src16 = Dqn_Win_String8ToString16(scratch.arena, src); - Dqn_String16 dest16 = Dqn_Win_String8ToString16(scratch.arena, dest); + Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src); + Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest); unsigned long flags = MOVEFILE_COPY_ALLOWED; if (overwrite) { @@ -295,16 +280,14 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) Dqn_ThreadScratch inner_scratch = Dqn_Thread_GetScratch(scratch.arena); Dqn_WinError error = Dqn_Win_LastError(inner_scratch.arena); Dqn_Log_ErrorF("Failed to move the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s", - DQN_STRING_FMT(src), - DQN_STRING_FMT(dest), - DQN_STRING_FMT(error.msg)); + DQN_STR_FMT(src), + DQN_STR_FMT(dest), + DQN_STR_FMT(error.msg)); } - - #elif defined(DQN_OS_UNIX) + #else // See: https://github.com/gingerBill/gb/blob/master/gb.h bool file_moved = true; - if (link(src.data, dest.data) == -1) - { + if (link(src.data, dest.data) == -1) { // NOTE: Link can fail if we're trying to link across different volumes // so we fall back to a binary directory. file_moved |= Dqn_Fs_Copy(src, dest, overwrite); @@ -312,44 +295,39 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite) if (file_moved) result = (unlink(src.data) != -1); // Remove original file - - #else - #error Unimplemented #endif return result; } -DQN_API bool Dqn_Fs_Delete(Dqn_String8 path) +DQN_API bool Dqn_Fs_Delete(Dqn_Str8 path) { bool result = false; - if (!Dqn_String8_IsValid(path)) + if (!Dqn_Str8_IsValid(path)) return result; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); if (path16.size) { result = DeleteFileW(path16.data); if (!result) result = RemoveDirectoryW(path16.data); } - #elif defined(DQN_OS_UNIX) - result = remove(path.data) == 0; #else - #error Unimplemented + result = remove(path.data) == 0; #endif return result; } // NOTE: R/W Entire File =========================================================================== -DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator) +DQN_API char *Dqn_Fs_ReadCStr8(char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator) { char *result = nullptr; if (!path) return result; if (path_size <= 0) - path_size = Dqn_CString8_Size(path); + path_size = Dqn_CStr8_Size(path); (void)allocator; (void)file_size; @@ -357,8 +335,8 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi #if defined(DQN_OS_WIN32) // NOTE: Convert to UTF16 ================================================== Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); - Dqn_String8 path8 = Dqn_String8_Init(path, path_size); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path8); + Dqn_Str8 path8 = Dqn_Str8_Init(path, path_size); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path8); // NOTE: Get the file handle =============================================== void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, @@ -370,7 +348,7 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi /*HANDLE hTemplateFile*/ nullptr); if (file_handle == INVALID_HANDLE_VALUE) { 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_STR_FMT(path8), DQN_STR_FMT(error.msg)); return nullptr; } DQN_DEFER { CloseHandle(file_handle); }; @@ -380,7 +358,7 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi LARGE_INTEGER win_file_size; if (!GetFileSizeEx(file_handle, &win_file_size)) { Dqn_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_STR_FMT(path8), DQN_STR_FMT(error.msg)); return nullptr; } @@ -406,7 +384,7 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi if (read_result == 0) { Dqn_Allocator_Dealloc(allocator, result, bytes_desired); Dqn_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_STR_FMT(path8), DQN_STR_FMT(error.msg)); return nullptr; } @@ -414,10 +392,10 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Allocator_Dealloc(allocator, result, bytes_desired); Dqn_Log_ErrorF("'ReadFile' failed to read all bytes into file [file=%.*s, bytes_desired=%u, bytes_read=%u, reason=%.*s]", - DQN_STRING_FMT(path8), + DQN_STR_FMT(path8), bytes_desired, bytes_read, - DQN_STRING_FMT(error.msg)); + DQN_STR_FMT(error.msg)); return nullptr; } @@ -440,7 +418,7 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi *file_size = ftell(file_handle); if (DQN_CAST(long)(*file_size) == -1L) { - Dqn_Log_ErrorF("Failed to determine '%s' file size using ftell\n", file); + Dqn_Log_ErrorF("Failed to determine '%s' file size using ftell\n", path); return result; } @@ -450,29 +428,29 @@ DQN_API char *Dqn_Fs_ReadCString8(char const *path, Dqn_usize path_size, Dqn_usi alignof(char), Dqn_ZeroMem_No); if (!result) { - Dqn_Log_ErrorF("Failed to allocate %td bytes to read file '%s'\n", *file_size + 1, file); + Dqn_Log_ErrorF("Failed to allocate %zu bytes to read file '%s'\n", *file_size + 1, path); return result; } result[*file_size] = 0; if (fread(result, DQN_CAST(size_t)(*file_size), 1, file_handle) != 1) { Dqn_Allocator_Dealloc(allocator, result, *file_size); - Dqn_Log_ErrorF("Failed to read %td bytes into buffer from '%s'\n", *file_size, file); + Dqn_Log_ErrorF("Failed to read %zu bytes into buffer from '%s'\n", *file_size, path); return result; } #endif return result; } -DQN_API Dqn_String8 Dqn_Fs_Read(Dqn_String8 path, Dqn_Allocator allocator) +DQN_API Dqn_Str8 Dqn_Fs_Read(Dqn_Str8 path, Dqn_Allocator allocator) { - Dqn_usize file_size = 0; - char * string = Dqn_Fs_ReadCString8(path.data, path.size, &file_size, allocator); - Dqn_String8 result = Dqn_String8_Init(string, file_size); + Dqn_usize file_size = 0; + char *string = Dqn_Fs_ReadCStr8(path.data, path.size, &file_size, allocator); + Dqn_Str8 result = Dqn_Str8_Init(string, file_size); return result; } -DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char const *buffer, Dqn_usize buffer_size) +DQN_API bool Dqn_Fs_WriteCStr8(char const *path, Dqn_usize path_size, char const *buffer, Dqn_usize buffer_size) { bool result = false; if (!path || !buffer || buffer_size <= 0) @@ -480,11 +458,11 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co #if defined(DQN_OS_WIN32) if (path_size <= 0) - path_size = Dqn_CString8_Size(path); + path_size = Dqn_CStr8_Size(path); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 path8 = Dqn_String8_Init(path, path_size); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path8); + Dqn_Str8 path8 = Dqn_Str8_Init(path, path_size); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path8); void *file_handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ GENERIC_WRITE, @@ -496,7 +474,7 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co if (file_handle == INVALID_HANDLE_VALUE) { 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)); + Dqn_Log_ErrorF("Failed to open file for writing [file=%.*s, reason=%.*s]", DQN_STR_FMT(path8), DQN_STR_FMT(error.msg)); return result; } DQN_DEFER { CloseHandle(file_handle); }; @@ -509,8 +487,7 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co // TODO(dqn): Use OS apis (void)path_size; - FILE *file_handle = nullptr; - fopen_s(&file_handle, path, "w+b"); + FILE *file_handle = fopen(path, "w+b"); if (!file_handle) { Dqn_Log_ErrorF("Failed to 'fopen' to get the file handle [file=%s]", path); return result; @@ -525,17 +502,17 @@ DQN_API bool Dqn_Fs_WriteCString8(char const *path, Dqn_usize path_size, char co #endif } -DQN_API bool Dqn_Fs_Write(Dqn_String8 file_path, Dqn_String8 buffer) +DQN_API bool Dqn_Fs_Write(Dqn_Str8 file_path, Dqn_Str8 buffer) { - bool result = Dqn_Fs_WriteCString8(file_path.data, file_path.size, buffer.data, buffer.size); + bool result = Dqn_Fs_WriteCStr8(file_path.data, file_path.size, buffer.data, buffer.size); return result; } // NOTE: R/W Stream API ============================================================================ -DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access) +DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_Str8 path, Dqn_FsFileOpen open_mode, uint32_t access) { Dqn_FsFile result = {}; - if (!Dqn_String8_IsValid(path) || path.size <= 0) + if (!Dqn_Str8_IsValid(path) || path.size <= 0) return result; if ((access & ~Dqn_FsFileAccess_All) || ((access & Dqn_FsFileAccess_All) == 0)) { @@ -567,14 +544,14 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u } Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String16 path16 = Dqn_Win_String8ToString16(scratch.arena, path); - void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, - /*DWORD dwDesiredAccess*/ access_mode, - /*DWORD dwShareMode*/ 0, - /*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ nullptr, - /*DWORD dwCreationDisposition*/ create_flag, - /*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_NORMAL, - /*HANDLE hTemplateFile*/ nullptr); + Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path); + void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, + /*DWORD dwDesiredAccess*/ access_mode, + /*DWORD dwShareMode*/ 0, + /*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ nullptr, + /*DWORD dwCreationDisposition*/ create_flag, + /*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_NORMAL, + /*HANDLE hTemplateFile*/ nullptr); if (handle == INVALID_HANDLE_VALUE) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); @@ -582,8 +559,8 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(result.error, DQN_ARRAY_UCOUNT(result.error), "Open file failed: %.*s for \"%.*s\"", - DQN_STRING_FMT(error.msg), - DQN_STRING_FMT(path)); + DQN_STR_FMT(error.msg), + DQN_STR_FMT(path)); return result; } #else @@ -592,7 +569,7 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u result.error, DQN_ARRAY_UCOUNT(result.error), "Open file failed: execute access not supported for \"%.*s\"", - DQN_STRING_FMT(path)); + DQN_STR_FMT(path)); DQN_INVALID_CODE_PATH; // TODO: Not supported via fopen return result; } @@ -602,20 +579,20 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u // before closing and reopening it if valid with the correct request access // permissions. { - void *handle = nullptr; + FILE *handle = nullptr; switch (open_mode) { - case Dqn_FsFileOpen_CreateAlways: handle = fopen(path, "w"); break; - case Dqn_FsFileOpen_OpenIfExist: handle = fopen(path, "r"); break; - case Dqn_FsFileOpen_OpenAlways: handle = fopen(path, "a"); break; + case Dqn_FsFileOpen_CreateAlways: handle = fopen(path.data, "w"); break; + case Dqn_FsFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break; + case Dqn_FsFileOpen_OpenAlways: handle = fopen(path.data, "a"); break; default: DQN_INVALID_CODE_PATH; break; } if (!handle) { - result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintF2DotsOnOverflow( + result.error_size = DQN_CAST(uint16_t)Dqn_SNPrintFDotTruncate( result.error, DQN_ARRAY_UCOUNT(result.error), "Open file failed: Could not open file in requested mode %d for \"%.*s\"", open_mode, - DQN_STRING_FMT(path)); + DQN_STR_FMT(path)); return result; } fclose(handle); @@ -630,14 +607,14 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u fopen_mode = "r+"; } - void *handle = fopen(path, fopen_mode); + FILE *handle = fopen(path.data, fopen_mode); if (!handle) { - result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintF2DotsOnOverflow( + result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate( result.error, DQN_ARRAY_UCOUNT(result.error), "Open file failed: Could not open file in fopen mode \"%s\" for \"%.*s\"", fopen_mode, - DQN_STRING_FMT(path)); + DQN_STR_FMT(path)); return result; } #endif @@ -666,33 +643,34 @@ DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *buffer, Dqn_us file->error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(file->error, DQN_ARRAY_UCOUNT(file->error), - "Write file failed: %.*s for %.*s", - DQN_STRING_FMT(error.msg)); + "Write file failed (%u): %.*s", + error.code, + DQN_STR_FMT(error.msg)); } #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*/, DQN_CAST(FILE *)file->handle) == 1 /*count*/; #endif return result; } -DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, Dqn_String8 buffer) +DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, Dqn_Str8 buffer) { bool result = Dqn_Fs_WriteFileBuffer(file, buffer.data, buffer.size); return result; } -DQN_API bool Dqn_Fs_WriteFileFV(Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API bool Dqn_Fs_WriteFileFV(Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args) { bool result = false; if (!file || !fmt) return result; - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 buffer = Dqn_String8_InitFV(scratch.allocator, fmt, args); - result = Dqn_Fs_WriteFileBuffer(file, buffer.data, buffer.size); - return result; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.allocator, fmt, args); + result = Dqn_Fs_WriteFileBuffer(file, buffer.data, buffer.size); + return result; } -DQN_API bool Dqn_Fs_WriteFileF(Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API bool Dqn_Fs_WriteFileF(Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -709,27 +687,27 @@ DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file) #if defined(DQN_OS_WIN32) CloseHandle(file->handle); #else - fclose(file->handle); + fclose(DQN_CAST(FILE *)file->handle); #endif *file = {}; } #endif // !defined(DQN_NO_FS) -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_Str8 path) { - if (!arena || !fs_path || !Dqn_String8_IsValid(path)) + if (!arena || !fs_path || !Dqn_Str8_IsValid(path)) return false; if (path.size <= 0) return true; - Dqn_String8 const delimiter_array[] = { - DQN_STRING8("\\"), - DQN_STRING8("/") + Dqn_Str8 const delimiter_array[] = { + DQN_STR8("\\"), + DQN_STR8("/") }; for (;;) { - Dqn_String8BinarySplitResult delimiter = Dqn_String8_BinarySplitArray(path, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array)); - for (; delimiter.lhs.data; delimiter = Dqn_String8_BinarySplitArray(delimiter.rhs, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array))) { + Dqn_Str8BinarySplitResult delimiter = Dqn_Str8_BinarySplitArray(path, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array)); + for (; delimiter.lhs.data; delimiter = Dqn_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array))) { if (delimiter.lhs.size <= 0) continue; @@ -756,18 +734,18 @@ DQN_API bool Dqn_FsPath_AddRef(Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String return true; } -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_Str8 path) { - Dqn_String8 copy = Dqn_String8_Copy(Dqn_Arena_Allocator(arena), path); - bool result = Dqn_FsPath_AddRef(arena, fs_path, copy); + Dqn_Str8 copy = Dqn_Str8_Copy(Dqn_Arena_Allocator(arena), path); + bool result = Dqn_FsPath_AddRef(arena, fs_path, copy); return result; } -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_AddF(Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); - Dqn_String8 path = Dqn_String8_InitFV(Dqn_Arena_Allocator(arena), fmt, args); + Dqn_Str8 path = Dqn_Str8_InitFV(Dqn_Arena_Allocator(arena), fmt, args); va_end(args); bool result = Dqn_FsPath_AddRef(arena, fs_path, path); return result; @@ -795,38 +773,38 @@ DQN_API bool Dqn_FsPath_Pop(Dqn_FsPath *fs_path) return true; } -DQN_API Dqn_String8 Dqn_FsPath_Convert(Dqn_Arena *arena, Dqn_String8 path) +DQN_API Dqn_Str8 Dqn_FsPath_Convert(Dqn_Arena *arena, Dqn_Str8 path) { Dqn_FsPath fs_path = {}; Dqn_FsPath_AddRef(arena, &fs_path, path); - Dqn_String8 result = Dqn_FsPath_Build(arena, &fs_path); + Dqn_Str8 result = Dqn_FsPath_Build(arena, &fs_path); return result; } -DQN_API Dqn_String8 Dqn_FsPath_ConvertF(Dqn_Arena *arena, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API Dqn_Str8 Dqn_FsPath_ConvertF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); va_list args; va_start(args, fmt); - Dqn_String8 path = Dqn_String8_InitFV(scratch.allocator, fmt, args); + Dqn_Str8 path = Dqn_Str8_InitFV(scratch.allocator, fmt, args); va_end(args); - Dqn_String8 result = Dqn_FsPath_Convert(arena, path); + Dqn_Str8 result = Dqn_FsPath_Convert(arena, path); return result; } -DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator) +DQN_API Dqn_Str8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_Str8 path_separator) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; if (!fs_path || fs_path->links_size <= 0) return result; // NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\' Dqn_usize string_size = fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size); - result = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), string_size, Dqn_ZeroMem_No); + result = Dqn_Str8_Allocate(Dqn_Arena_Allocator(arena), string_size, Dqn_ZeroMem_No); if (result.data) { char *dest = result.data; for (Dqn_FsPathLink *link = fs_path->head; link; link = link->next) { - Dqn_String8 string = link->string; + Dqn_Str8 string = link->string; DQN_MEMCPY(dest, string.data, string.size); dest += string.size; @@ -842,7 +820,7 @@ DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath c } // NOTE: [$DATE] Dqn_Date ========================================================================== -DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow() +DQN_API Dqn_DateHMSTime Dqn_Date_LocalTimeHMSNow() { Dqn_DateHMSTime result = {}; #if defined(DQN_OS_WIN32) @@ -882,17 +860,17 @@ DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow() return result; } -DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString(Dqn_DateHMSTime time, char date_separator, char hms_separator) +DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8(Dqn_DateHMSTime time, char date_separator, char hms_separator) { - Dqn_DateHMSTimeString result = {}; - result.hms_size = DQN_CAST(uint8_t) STB_SPRINTF_DECORATE(snprintf)(result.hms, - DQN_ARRAY_ICOUNT(result.hms), - "%02d%c%02d%c%02d", - time.hour, - hms_separator, - time.minutes, - hms_separator, - time.seconds); + Dqn_DateHMSTimeStr8 result = {}; + result.hms_size = DQN_CAST(uint8_t) STB_SPRINTF_DECORATE(snprintf)(result.hms, + DQN_ARRAY_ICOUNT(result.hms), + "%02d%c%02d%c%02d", + time.hour, + hms_separator, + time.minutes, + hms_separator, + time.seconds); result.date_size = DQN_CAST(uint8_t) STB_SPRINTF_DECORATE(snprintf)(result.date, DQN_ARRAY_ICOUNT(result.date), @@ -908,33 +886,29 @@ DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString(Dqn_DateHMSTime time, return result; } -DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeStringNow(char date_separator, char hms_separator) +DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8Now(char date_separator, char hms_separator) { - Dqn_DateHMSTime time = Dqn_Date_HMSLocalTimeNow(); - Dqn_DateHMSTimeString result = Dqn_Date_HMSLocalTimeString(time, date_separator, hms_separator); + Dqn_DateHMSTime time = Dqn_Date_LocalTimeHMSNow(); + Dqn_DateHMSTimeStr8 result = Dqn_Date_LocalTimeHMSStr8(time, date_separator, hms_separator); return result; } DQN_API uint64_t Dqn_Date_EpochTime() { -#if defined(DQN_OS_WIN32) - const uint64_t UNIX_TIME_START = 0x019DB1DED53E8000; //January 1, 1970 (start of Unix epoch) in "ticks" - const uint64_t TICKS_PER_SECOND = 10'000'000; // Filetime returned is in intervals of 100 nanoseconds + #if defined(DQN_OS_WIN32) + const uint64_t UNIX_TIME_START = 0x019DB1DED53E8000; //January 1, 1970 (start of Unix epoch) in "ticks" + const uint64_t TICKS_PER_SECOND = 10'000'000; // Filetime returned is in intervals of 100 nanoseconds - FILETIME file_time; - GetSystemTimeAsFileTime(&file_time); + FILETIME file_time; + GetSystemTimeAsFileTime(&file_time); - LARGE_INTEGER date_time; - date_time.u.LowPart = file_time.dwLowDateTime; - date_time.u.HighPart = file_time.dwHighDateTime; - uint64_t result = (date_time.QuadPart - UNIX_TIME_START) / TICKS_PER_SECOND; - -#elif defined(DQN_OS_UNIX) + LARGE_INTEGER date_time; + date_time.u.LowPart = file_time.dwLowDateTime; + date_time.u.HighPart = file_time.dwHighDateTime; + uint64_t result = (date_time.QuadPart - UNIX_TIME_START) / TICKS_PER_SECOND; + #else uint64_t result = time(nullptr); -#else - #error Unimplemented -#endif - + #endif return result; } @@ -944,7 +918,7 @@ DQN_API uint64_t Dqn_Date_EpochTime() DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena) { Dqn_WinError result = {}; - result.code = GetLastError(); + result.code = GetLastError(); if (arena) { unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; void *module_to_get_errors_from = nullptr; @@ -963,18 +937,17 @@ DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena) nullptr); // va_list * Arguments if (size) { - Dqn_String8 buffer = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), size, Dqn_ZeroMem_No); - int32_t buffer_size = DQN_CAST(int32_t)buffer.size; - int32_t new_size = FormatMessageA(flags, - module_to_get_errors_from, // LPCVOID lpSource, - result.code, // unsigned long dwMessageId, - 0, // unsigned long dwLanguageId, - buffer.data, // LPSTR lpBuffer, - buffer_size, // unsigned long nSize, - nullptr); // va_list * Arguments - if (DQN_CHECK(new_size == size)) { + Dqn_Str8 buffer = Dqn_Str8_Allocate(Dqn_Arena_Allocator(arena), size, Dqn_ZeroMem_No); + int32_t buffer_size = DQN_CAST(int32_t) buffer.size; + int32_t new_size = FormatMessageA(flags, + module_to_get_errors_from, // LPCVOID lpSource, + result.code, // unsigned long dwMessageId, + 0, // unsigned long dwLanguageId, + buffer.data, // LPSTR lpBuffer, + buffer_size, // unsigned long nSize, + nullptr); // va_list * Arguments + if (DQN_CHECK(new_size == size)) result.msg = buffer; - } } } return result; @@ -1003,11 +976,11 @@ DQN_API void Dqn_Win_MakeProcessDPIAware() } } -// NOTE: Windows UTF8 to String16 ============================================== -DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src) +// NOTE: Windows UTF8 to Str16 ============================================== +DQN_API Dqn_Str16 Dqn_Win_Str8ToStr16(Dqn_Arena *arena, Dqn_Str8 src) { - Dqn_String16 result = {}; - if (!arena || !Dqn_String8_IsValid(src)) + Dqn_Str16 result = {}; + if (!arena || !Dqn_Str8_IsValid(src)) return result; int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, nullptr /*dest*/, 0 /*dest size*/); @@ -1027,10 +1000,10 @@ DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src return result; } -DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String8 src, wchar_t *dest, int dest_size) +DQN_API int Dqn_Win_Str8ToStr16Buffer(Dqn_Str8 src, wchar_t *dest, int dest_size) { int result = 0; - if (!Dqn_String8_IsValid(src)) + if (!Dqn_Str8_IsValid(src)) return result; result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DQN_CAST(int)src.size, nullptr /*dest*/, 0 /*dest size*/); @@ -1042,11 +1015,11 @@ DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String8 src, wchar_t *dest, int return result; } -// NOTE: Windows String16 To UTF8 ================================================================== -DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, int dest_size) +// NOTE: Windows Str16 To UTF8 ================================================================== +DQN_API int Dqn_Win_Str16ToStr8Buffer(Dqn_Str16 src, char *dest, int dest_size) { int result = 0; - if (!Dqn_String8_IsValid(src)) + if (!Dqn_Str8_IsValid(src)) return result; int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); @@ -1062,10 +1035,10 @@ DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, int de return result; } -DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src) +DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8(Dqn_Arena *arena, Dqn_Str16 src) { - Dqn_String8 result = {}; - if (!arena || !Dqn_String8_IsValid(src)) + Dqn_Str8 result = {}; + if (!arena || !Dqn_Str8_IsValid(src)) return result; int src_size = Dqn_Safe_SaturateCastISizeToInt(src.size); @@ -1076,11 +1049,11 @@ DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src if (required_size <= 0) return result; - // NOTE: String allocate ensures there's one extra byte for + // NOTE: Str8 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)) + Dqn_Str8 buffer = Dqn_Str8_Allocate(Dqn_Arena_Allocator(arena), required_size, Dqn_ZeroMem_No); + if (!Dqn_Str8_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); @@ -1096,10 +1069,36 @@ DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src } // NOTE: Windows Executable Directory ========================================== -DQN_API Dqn_String16 Dqn_Win_EXEDirW(Dqn_Arena *arena) +DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); - Dqn_String16 result = {}; + Dqn_Str16 result = {}; + Dqn_usize module_size = 0; + wchar_t *module_path = nullptr; + do { + module_size += 256; + module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No); + 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, module_size + 1, Dqn_ZeroMem_No); + result.size = module_size; + DQN_MEMCPY(result.data, module_path, sizeof(wchar_t) * result.size); + result.data[result.size] = 0; + return result; +} + +DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena) +{ + // TODO(doyle): Implement a Dqn_Str16_BinarySearchReverse + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); + Dqn_Str16 result = {}; Dqn_usize module_size = 0; wchar_t *module_path = nullptr; do { @@ -1121,19 +1120,19 @@ DQN_API Dqn_String16 Dqn_Win_EXEDirW(Dqn_Arena *arena) return result; } -DQN_API Dqn_String8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_String8 suffix) +DQN_API Dqn_Str8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_Str8 suffix) { - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); - Dqn_String16 suffix16 = Dqn_Win_String8ToString16(scratch.arena, suffix); - Dqn_String16 dir16 = Dqn_Win_WorkingDirW(Dqn_Arena_Allocator(scratch.arena), suffix16); - Dqn_String8 result = Dqn_Win_String16ToString8(scratch.arena, dir16); + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); + Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(scratch.arena, suffix); + Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(Dqn_Arena_Allocator(scratch.arena), suffix16); + Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, dir16); return result; } -DQN_API Dqn_String16 Dqn_Win_WorkingDirW(Dqn_Allocator allocator, Dqn_String16 suffix) +DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Allocator allocator, Dqn_Str16 suffix) { DQN_ASSERT(suffix.size >= 0); - Dqn_String16 result = {}; + Dqn_Str16 result = {}; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); @@ -1161,11 +1160,11 @@ DQN_API Dqn_String16 Dqn_Win_WorkingDirW(Dqn_Allocator allocator, Dqn_String16 s w_path[desired_size] = 0; } - result = Dqn_String16{w_path, DQN_CAST(Dqn_usize)(desired_size - 1)}; + result = Dqn_Str16{w_path, DQN_CAST(Dqn_usize)(desired_size - 1)}; return result; } -DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it) +DQN_API bool Dqn_Win_FolderWIterate(Dqn_Str16 path, Dqn_Win_FolderIteratorW *it) { WIN32_FIND_DATAW find_data = {}; if (it->handle) { @@ -1184,13 +1183,13 @@ DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW * } it->file_name_buf[0] = 0; - it->file_name = Dqn_String16{it->file_name_buf, 0}; + it->file_name = Dqn_Str16{it->file_name_buf, 0}; do { if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.')) continue; - it->file_name.size = Dqn_CString16_Size(find_data.cFileName); + it->file_name.size = Dqn_CStr16_Size(find_data.cFileName); DQN_ASSERT(it->file_name.size < (DQN_ARRAY_UCOUNT(it->file_name_buf) - 1)); DQN_MEMCPY(it->file_name.data, find_data.cFileName, it->file_name.size * sizeof(wchar_t)); it->file_name_buf[it->file_name.size] = 0; @@ -1200,34 +1199,34 @@ DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW * return it->file_name.size > 0; } -DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) +DQN_API bool Dqn_Win_FolderIterate(Dqn_Str8 path, Dqn_Win_FolderIterator *it) { - if (!Dqn_String8_IsValid(path) || !it || path.size <= 0) + if (!Dqn_Str8_IsValid(path) || !it || path.size <= 0) return false; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Win_FolderIteratorW wide_it = {}; - Dqn_String16 path16 = {}; + Dqn_Str16 path16 = {}; if (it->handle) { wide_it.handle = it->handle; } else { - bool needs_asterisks = Dqn_String8_EndsWith(path, DQN_STRING8("\\")) || - Dqn_String8_EndsWith(path, DQN_STRING8("/")); - bool has_glob = Dqn_String8_EndsWith(path, DQN_STRING8("\\*")) || - Dqn_String8_EndsWith(path, DQN_STRING8("/*")); + bool needs_asterisks = Dqn_Str8_EndsWith(path, DQN_STR8("\\")) || + Dqn_Str8_EndsWith(path, DQN_STR8("/")); + bool has_glob = Dqn_Str8_EndsWith(path, DQN_STR8("\\*")) || + Dqn_Str8_EndsWith(path, DQN_STR8("/*")); - Dqn_String8 adjusted_path = path; + Dqn_Str8 adjusted_path = path; if (!has_glob) { // NOTE: We are missing the glob for enumerating the files, we will // add those characters in this branch, so overwrite the null // character, add the glob and re-null terminate the buffer. if (needs_asterisks) - adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s*", DQN_STRING_FMT(path)); + adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s*", DQN_STR_FMT(path)); else - adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/*", DQN_STRING_FMT(path)); + adjusted_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/*", DQN_STR_FMT(path)); } - path16 = Dqn_Win_String8ToString16(scratch.arena, adjusted_path); + path16 = Dqn_Win_Str8ToStr16(scratch.arena, adjusted_path); if (path16.size <= 0) // Conversion error return false; } @@ -1235,8 +1234,8 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) bool result = Dqn_Win_FolderWIterate(path16, &wide_it); it->handle = wide_it.handle; if (result) { - 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); + int size = Dqn_Win_Str16ToStr8Buffer(wide_it.file_name, it->file_name_buf, DQN_ARRAY_UCOUNT(it->file_name_buf)); + it->file_name = Dqn_Str8_Init(it->file_name_buf, size); } return result; @@ -1245,7 +1244,7 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) #if !defined(DQN_NO_WINNET) // NOTE: [$WINN] Dqn_WinNet ======================================================================== -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_size) +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCStr8(char const *url, int url_size) { URL_COMPONENTSA components = {}; components.dwStructSize = sizeof(components); @@ -1257,7 +1256,7 @@ DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_s if (!InternetCrackUrlA(url, url_size, 0 /*flags*/, &components)) { 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)); + Dqn_Log_ErrorF("InternetCrackUrlA failed [reason=%.*s]", DQN_STR_FMT(error.msg)); return result; } @@ -1272,7 +1271,7 @@ DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_s } if (components.dwHostNameLength > (DQN_ARRAY_UCOUNT(result.host_name) - 1)) { - Dqn_Log_ErrorF("Host name is longer than the maximum supported [max=%d]", DQN_ARRAY_UCOUNT(result.host_name) - 1); + Dqn_Log_ErrorF("Host name is longer than the maximum supported [max=%zu]", DQN_ARRAY_UCOUNT(result.host_name) - 1); return result; } @@ -1303,20 +1302,20 @@ DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_s return result; } -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit(Dqn_String8 url) +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit(Dqn_Str8 url) { - Dqn_WinNetHandle result = Dqn_Win_NetHandleInitCString(url.data, DQN_CAST(int)url.size); + Dqn_WinNetHandle result = Dqn_Win_NetHandleInitCStr8(url.data, DQN_CAST(int)url.size); return result; } -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCString(char const *url, int url_size, char const *http_method) +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCStr8(char const *url, int url_size, char const *http_method) { - Dqn_WinNetHandle result = Dqn_Win_NetHandleInitCString(url, url_size); + Dqn_WinNetHandle result = Dqn_Win_NetHandleInitCStr8(url, url_size); Dqn_Win_NetHandleSetHTTPMethod(&result, http_method); return result; } -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod(Dqn_String8 url, Dqn_String8 http_method) +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod(Dqn_Str8 url, Dqn_Str8 http_method) { Dqn_WinNetHandle result = Dqn_Win_NetHandleInit(url); Dqn_Win_NetHandleSetHTTPMethod(&result, http_method.data); @@ -1342,7 +1341,7 @@ DQN_API bool Dqn_Win_NetHandleIsValid(Dqn_WinNetHandle const *handle) return result; } -DQN_API void Dqn_Win_NetHandleSetUserAgentCString(Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size) +DQN_API void Dqn_Win_NetHandleSetUserAgentCStr8(Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size) { if (!Dqn_Win_NetHandleIsValid(handle)) return; @@ -1377,7 +1376,7 @@ DQN_API bool Dqn_Win_NetHandleSetHTTPMethod(Dqn_WinNetHandle *handle, return true; } -DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle, +DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCStr8(Dqn_WinNetHandle *handle, char const *header, uint32_t header_size, uint32_t mode) @@ -1417,9 +1416,9 @@ DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle, return result; } -DQN_API bool Dqn_Win_NetHandleSetRequestHeaderString8(Dqn_WinNetHandle *handle, Dqn_String8 header, uint32_t mode) +DQN_API bool Dqn_Win_NetHandleSetRequestHeaderStr8(Dqn_WinNetHandle *handle, Dqn_Str8 header, uint32_t mode) { - bool result = Dqn_Win_NetHandleSetRequestHeaderCString8(handle, header.data, Dqn_Safe_SaturateCastISizeToUInt(header.size), mode); + bool result = Dqn_Win_NetHandleSetRequestHeaderCStr8(handle, header.data, Dqn_Safe_SaturateCastISizeToUInt(header.size), mode); return result; } @@ -1442,7 +1441,7 @@ DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle * Dqn_Log_ErrorF("Failed to send request to %.*s [reason=%.*s]", handle->host_name_size, handle->host_name, - DQN_STRING_FMT(error.msg)); + DQN_STR_FMT(error.msg)); return result; } @@ -1452,7 +1451,7 @@ DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle * if (!DQN_CHECK(query_result != ERROR_INSUFFICIENT_BUFFER)) return result; - result.raw_headers = Dqn_String8_Allocate(allocator, buffer_size, Dqn_ZeroMem_No); + result.raw_headers = Dqn_Str8_Allocate(allocator, buffer_size, Dqn_ZeroMem_No); if (!result.raw_headers.data) return result; @@ -1462,34 +1461,34 @@ DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle * return result; } - Dqn_String8 delimiter = DQN_STRING8("\r\n"); - Dqn_usize splits_required = Dqn_String8_Split(result.raw_headers, delimiter, nullptr, 0); - result.headers = Dqn_Allocator_NewArray(allocator, Dqn_String8, splits_required, Dqn_ZeroMem_No); - result.headers_size = Dqn_String8_Split(result.raw_headers, delimiter, result.headers, splits_required); + Dqn_Str8 delimiter = DQN_STR8("\r\n"); + Dqn_usize splits_required = Dqn_Str8_Split(result.raw_headers, delimiter, nullptr, 0); + result.headers = Dqn_Allocator_NewArray(allocator, Dqn_Str8, splits_required, Dqn_ZeroMem_No); + result.headers_size = Dqn_Str8_Split(result.raw_headers, delimiter, result.headers, splits_required); bool found_content_type = false; bool found_content_length = false; for (Dqn_usize header_index = 0; header_index < result.headers_size; header_index++) { - Dqn_String8 header = result.headers[header_index]; + Dqn_Str8 header = result.headers[header_index]; - Dqn_String8BinarySplitResult key_value_split = Dqn_String8_BinarySplit(header, DQN_STRING8(":")); - Dqn_String8 value = key_value_split.lhs; - Dqn_String8 key = key_value_split.rhs; + Dqn_Str8BinarySplitResult key_value_split = Dqn_Str8_BinarySplit(header, DQN_STR8(":")); + Dqn_Str8 value = key_value_split.lhs; + Dqn_Str8 key = key_value_split.rhs; - key = Dqn_String8_TrimWhitespaceAround(key); - value = Dqn_String8_TrimWhitespaceAround(value); + key = Dqn_Str8_TrimWhitespaceAround(key); + value = Dqn_Str8_TrimWhitespaceAround(value); - if (Dqn_String8_EqInsensitive(key, DQN_STRING8("Content-Type"))) { + if (Dqn_Str8_EqInsensitive(key, DQN_STR8("Content-Type"))) { DQN_ASSERT(!found_content_type); if (!found_content_type) { found_content_type = true; result.content_type = value; } - } else if (Dqn_String8_EqInsensitive(key, DQN_STRING8("Content-Length"))) { + } else if (Dqn_Str8_EqInsensitive(key, DQN_STR8("Content-Length"))) { DQN_ASSERT(!found_content_length); if (!found_content_length) { found_content_length = true; - result.content_length = Dqn_String8_ToU64(value, 0 /*separator*/).value; + result.content_length = Dqn_Str8_ToU64(value, 0 /*separator*/).value; } } @@ -1544,7 +1543,7 @@ struct Dqn_Win_NetChunk Dqn_Win_NetChunk *next; }; -DQN_API char *Dqn_Win_NetHandlePumpCString8(Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size) +DQN_API char *Dqn_Win_NetHandlePumpCStr8(Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size) { if (handle->state != Dqn_WinNetHandleState_RequestGood) return nullptr; @@ -1582,11 +1581,11 @@ DQN_API char *Dqn_Win_NetHandlePumpCString8(Dqn_WinNetHandle *handle, Dqn_Arena return result; } -DQN_API Dqn_String8 Dqn_Win_NetHandlePumpString8(Dqn_WinNetHandle *handle, Dqn_Arena *arena) +DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpStr8(Dqn_WinNetHandle *handle, Dqn_Arena *arena) { - size_t size = 0; - char * download = Dqn_Win_NetHandlePumpCString8(handle, arena, &size); - Dqn_String8 result = Dqn_String8_Init(download, size); + size_t size = 0; + char *download = Dqn_Win_NetHandlePumpCStr8(handle, arena, &size); + Dqn_Str8 result = Dqn_Str8_Init(download, size); return result; } @@ -1600,7 +1599,7 @@ DQN_API void Dqn_Win_NetHandlePumpToCRTFile(Dqn_WinNetHandle *handle, FILE *file } } -DQN_API char *Dqn_Win_NetHandlePumpToAllocCString(Dqn_WinNetHandle *handle, size_t *download_size) +DQN_API char *Dqn_Win_NetHandlePumpToAllocCStr8(Dqn_WinNetHandle *handle, size_t *download_size) { size_t total_size = 0; Dqn_Win_NetChunk *first_chunk = nullptr; @@ -1638,11 +1637,11 @@ DQN_API char *Dqn_Win_NetHandlePumpToAllocCString(Dqn_WinNetHandle *handle, size return result; } -DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString(Dqn_WinNetHandle *handle) +DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpToAllocStr8(Dqn_WinNetHandle *handle) { - size_t download_size = 0; - char * download = Dqn_Win_NetHandlePumpToAllocCString(handle, &download_size); - Dqn_String8 result = Dqn_String8_Init(download, download_size); + size_t download_size = 0; + char *download = Dqn_Win_NetHandlePumpToAllocCStr8(handle, &download_size); + Dqn_Str8 result = Dqn_Str8_Init(download, download_size); return result; } #endif // !defined(DQN_NO_WINNET) @@ -1701,20 +1700,17 @@ DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size) } #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) -DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Arena *arena) +DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; if (!arena) return result; #if defined(DQN_OS_WIN32) - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); - Dqn_String16 exe_dir16 = Dqn_Win_EXEDirW(scratch.arena); - result = Dqn_Win_String16ToString8(arena, exe_dir16); - - #elif defined(DQN_OS_UNIX) - + Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(scratch.arena); + result = Dqn_Win_Str16ToStr8(arena, exe_dir16); + #else int required_size_wo_null_terminator = 0; for (int try_size = 128;; try_size *= 2) { auto scoped_arena = Dqn_ArenaTempMemoryScope(arena); @@ -1739,22 +1735,12 @@ DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Arena *arena) // time after this "calculate" step. DQN_ASSERTF(bytes_written < try_size, "bytes_written can never be greater than the try size, function writes at most try_size"); required_size_wo_null_terminator = bytes_written; - - for (Dqn_isize index_of_last_slash = bytes_written; index_of_last_slash >= 0; index_of_last_slash--) { - if (try_buf[index_of_last_slash] == '/') { - // NOTE: We take the index of the last slash and not - // (index_of_last_slash + 1) because we want to exclude the - // trailing backslash as a convention of this library. - required_size_wo_null_terminator = index_of_last_slash; - break; - } - } break; } } if (required_size_wo_null_terminator) { - Dqn_ArenaTempMemory scope = Dqn_Arena_BeginTempMemory(arena); + Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(arena); char *exe_path = Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No); exe_path[required_size_wo_null_terminator] = 0; @@ -1763,17 +1749,28 @@ DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Arena *arena) // Note that if read-link fails again can be because there's // a potential race condition here, our exe or directory could have // been deleted since the last call, so we need to be careful. - Dqn_Arena_EndTempMemory(scope); + Dqn_Arena_EndTempMemory(temp_memory, true /*cancel*/); } else { - result = Dqn_String8_Init(exe_path, required_size_wo_null_terminator); + result = Dqn_Str8_Init(exe_path, required_size_wo_null_terminator); } } - #else - #error Unimplemented #endif return result; } -#endif + +DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena) +{ + Dqn_Str8 result = {}; + if (!arena) + return result; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); + Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena); + Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators)); + result = Dqn_Str8_Copy(Dqn_Arena_Allocator(arena), split.lhs); + return result; +} +#endif // (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) DQN_API void Dqn_OS_SleepMs(Dqn_uint milliseconds) { @@ -1916,12 +1913,15 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_f uint64_t os_frequency = Dqn_OS_PerfCounterFrequency(); uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL; uint64_t tsc_begin = Dqn_CPU_TSC(); - uint64_t os_elapsed = 0; - for (uint64_t os_begin = Dqn_OS_PerfCounterNow(); os_elapsed < os_target_elapsed; ) - os_elapsed = Dqn_OS_PerfCounterNow() - os_begin; - uint64_t tsc_end = Dqn_CPU_TSC(); - uint64_t tsc_elapsed = tsc_end - tsc_begin; - uint64_t result = tsc_elapsed / os_elapsed * os_frequency; + uint64_t result = 0; + if (tsc_begin) { + uint64_t os_elapsed = 0; + for (uint64_t os_begin = Dqn_OS_PerfCounterNow(); os_elapsed < os_target_elapsed; ) + os_elapsed = Dqn_OS_PerfCounterNow() - os_begin; + uint64_t tsc_end = Dqn_CPU_TSC(); + uint64_t tsc_elapsed = tsc_end - tsc_begin; + result = tsc_elapsed / os_elapsed * os_frequency; + } return result; } @@ -1947,7 +1947,7 @@ DQN_API uint32_t Dqn_Thread_GetID() unsigned long result = GetCurrentThreadId(); #else pid_t result = gettid(); - assert(gettid() >= 0); + DQN_ASSERT(gettid() >= 0); #endif return (uint32_t)result; } @@ -1958,12 +1958,12 @@ DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext() if (!result.init) { result.init = true; Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog; - DQN_ASSERTF(g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init(nullptr)"); + DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()"); // NOTE: Setup scratch arenas for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(result.scratch_arenas); index++) { result.scratch_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog, - DQN_GIGABYTES(1) /*size*/, + DQN_MEGABYTES(16) /*size*/, DQN_KILOBYTES(64) /*commit*/, "Thread %u Scratch Arena %u", Dqn_Thread_GetID(), diff --git a/dqn_platform.h b/dqn_platform.h index 8fff68d..38da5b1 100644 --- a/dqn_platform.h +++ b/dqn_platform.h @@ -4,7 +4,7 @@ #endif // NOTE: [$FSYS] Dqn_Fs ============================================================================ // NOTE: FS Manipulation =========================================================================== -// TODO(dqn): We should have a Dqn_String8 interface and a CString interface +// TODO(dqn): We should have a Dqn_Str8 interface and a CStr8 interface // // NOTE: API ======================================================================================= // @proc Dqn_FsDelete @@ -29,26 +29,26 @@ struct Dqn_FsInfo uint64_t size; }; -DQN_API bool Dqn_Fs_Exists (Dqn_String8 path); -DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path); -DQN_API Dqn_FsInfo Dqn_Fs_GetInfo (Dqn_String8 path); -DQN_API bool Dqn_Fs_Copy (Dqn_String8 src, Dqn_String8 dest, bool overwrite); -DQN_API bool Dqn_Fs_MakeDir (Dqn_String8 path); -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_Exists (Dqn_Str8 path); +DQN_API bool Dqn_Fs_DirExists(Dqn_Str8 path); +DQN_API Dqn_FsInfo Dqn_Fs_GetInfo (Dqn_Str8 path); +DQN_API bool Dqn_Fs_Copy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite); +DQN_API bool Dqn_Fs_MakeDir (Dqn_Str8 path); +DQN_API bool Dqn_Fs_Move (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite); +DQN_API bool Dqn_Fs_Delete (Dqn_Str8 path); // NOTE: R/W Entire File =========================================================================== // NOTE: API ======================================================================================= -// @proc Dqn_Fs_WriteString8, Dqn_Fs_WriteCString8 +// @proc Dqn_Fs_WriteStr8, Dqn_Fs_WriteCStr8 // @desc Write the string to a file at the path overwriting if necessary. -// @proc Dqn_Fs_ReadString8, Dqn_Fs_ReadCString8 +// @proc Dqn_Fs_ReadStr8, Dqn_Fs_ReadCStr8 // @desc Read the file at the path to a string. -DQN_API bool Dqn_Fs_WriteCString8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size); -DQN_API bool Dqn_Fs_Write (Dqn_String8 file_path, Dqn_String8 buffer); -DQN_API char *Dqn_Fs_ReadCString8 (char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator); -DQN_API Dqn_String8 Dqn_Fs_Read (Dqn_String8 path, Dqn_Allocator allocator); +DQN_API bool Dqn_Fs_WriteCStr8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size); +DQN_API bool Dqn_Fs_Write (Dqn_Str8 file_path, Dqn_Str8 buffer); +DQN_API char *Dqn_Fs_ReadCStr8 (char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator); +DQN_API Dqn_Str8 Dqn_Fs_Read (Dqn_Str8 path, Dqn_Allocator allocator); // NOTE: R/W Stream API ============================================================================ // NOTE: API ======================================================================================= @@ -85,11 +85,11 @@ enum Dqn_FsFileAccess Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute, }; -DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access); +DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_Str8 path, Dqn_FsFileOpen open_mode, uint32_t access); DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *data, Dqn_usize size); -DQN_API bool Dqn_Fs_WriteFile (Dqn_FsFile *file, Dqn_String8 buffer); -DQN_API bool Dqn_Fs_WriteFileFV (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); -DQN_API bool Dqn_Fs_WriteFileF (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); +DQN_API bool Dqn_Fs_WriteFile (Dqn_FsFile *file, Dqn_Str8 buffer); +DQN_API bool Dqn_Fs_WriteFileFV (Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API bool Dqn_Fs_WriteFileF (Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, ...); DQN_API void Dqn_Fs_CloseFile (Dqn_FsFile *file); #endif // !defined(DQN_NO_FS) @@ -124,16 +124,17 @@ DQN_API void Dqn_Fs_CloseFile (Dqn_FsFile *file); #else #define Dqn_FsPathOSSeperator "/" #endif - #define Dqn_FsPathOSSeperatorString DQN_STRING8(Dqn_FsPathOSSeperator) + #define Dqn_FsPathOSSeperatorString DQN_STR8(Dqn_FsPathOSSeperator) #endif struct Dqn_FsPathLink { - Dqn_String8 string; + Dqn_Str8 string; Dqn_FsPathLink *next; Dqn_FsPathLink *prev; }; + struct Dqn_FsPath { Dqn_FsPathLink *head; @@ -142,15 +143,15 @@ struct Dqn_FsPath uint16_t links_size; }; -DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path); -DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path); -DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path); -DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator); -DQN_API Dqn_String8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_String8 path); -DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/")) -#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\")) +DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_Str8 path); +DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_Str8 path); +DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path); +DQN_API Dqn_Str8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_Str8 path_separator); +DQN_API Dqn_Str8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_Str8 path); +DQN_API Dqn_Str8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...); +#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STR8("/")) +#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STR8("\\")) #if !defined(Dqn_FsPath_Build) #if defined(DQN_OS_WIN32) @@ -166,7 +167,7 @@ DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_STRI // @desc Produce the time elapsed since the Unix epoch // (e.g. 1970-01-01T00:00:00Z) in seconds -struct Dqn_DateHMSTimeString +struct Dqn_DateHMSTimeStr8 { char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")]; uint8_t date_size; @@ -186,10 +187,10 @@ struct Dqn_DateHMSTime uint8_t seconds; }; -DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow (); -DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeStringNow(char date_separator = '-', char hms_separator = ':'); -DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString (Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':'); -DQN_API uint64_t Dqn_Date_EpochTime (); +DQN_API Dqn_DateHMSTime Dqn_Date_LocalTimeHMSNow (); +DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8Now(char date_separator = '-', char hms_separator = ':'); +DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8 (Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':'); +DQN_API uint64_t Dqn_Date_EpochTime (); #if defined(DQN_OS_WIN32) #if !defined(DQN_NO_WIN) @@ -207,12 +208,12 @@ DQN_API uint64_t Dqn_Date_EpochTime (); struct Dqn_WinError { unsigned long code; - Dqn_String8 msg; + Dqn_Str8 msg; }; DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena); DQN_API void Dqn_Win_MakeProcessDPIAware(); -// NOTE: Windows String8 <-> String16 =========================================== +// NOTE: Windows Str8 <-> Str16 =========================================== // Convert a UTF8 <-> UTF16 string. // // The exact size buffer required for this function can be determined by @@ -225,10 +226,10 @@ DQN_API void Dqn_Win_MakeProcessDPIAware(); // written/required for conversion. 0 if there was a conversion error and can be // queried using 'Dqn_Win_LastError' -DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src); -DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String16 src, char *dest, int dest_size); -DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src); -DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, int dest_size); +DQN_API Dqn_Str16 Dqn_Win_Str8ToStr16 (Dqn_Arena *arena, Dqn_Str8 src); +DQN_API int Dqn_Win_Str8ToStr16Buffer(Dqn_Str16 src, char *dest, int dest_size); +DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8 (Dqn_Arena *arena, Dqn_Str16 src); +DQN_API int Dqn_Win_Str16ToStr8Buffer(Dqn_Str16 src, char *dest, int dest_size); // NOTE: Path navigation =========================================================================== // NOTE: API ======================================================================================= @@ -256,22 +257,23 @@ DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *des struct Dqn_Win_FolderIteratorW { void *handle; - Dqn_String16 file_name; + Dqn_Str16 file_name; wchar_t file_name_buf[512]; }; struct Dqn_Win_FolderIterator { - void *handle; - Dqn_String8 file_name; - char file_name_buf[512]; + void *handle; + Dqn_Str8 file_name; + char file_name_buf[512]; }; -DQN_API Dqn_String16 Dqn_Win_EXEDirW (Dqn_Arena *arena); -DQN_API Dqn_String8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_String8 suffix); -DQN_API Dqn_String16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_String16 suffix); -DQN_API bool Dqn_Win_FolderIterate (Dqn_String8 path, Dqn_Win_FolderIterator *it); -DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it); +DQN_API Dqn_Str16 Dqn_Win_EXEPathW (Dqn_Arena *arena); +DQN_API Dqn_Str16 Dqn_Win_EXEDirW (Dqn_Arena *arena); +DQN_API Dqn_Str8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_Str8 suffix); +DQN_API Dqn_Str16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_Str16 suffix); +DQN_API bool Dqn_Win_FolderIterate (Dqn_Str8 path, Dqn_Win_FolderIterator *it); +DQN_API bool Dqn_Win_FolderWIterate(Dqn_Str16 path, Dqn_Win_FolderIteratorW *it); #endif // !defined(DQN_NO_WIN) #if !defined(DQN_NO_WINNET) @@ -284,7 +286,7 @@ DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_Folder // INTERNET_OPTION_SEND_TIMEOUT // // NOTE: API ======================================================================================= -// @proc Dqn_Win_NetHandleInitHTTPMethod, Dqn_Win_NetHandleInitHTTPMethodCString +// @proc Dqn_Win_NetHandleInitHTTPMethod, Dqn_Win_NetHandleInitHTTPMethodCStr8 // @desc Setup a handle to the URL with the given HTTP verb. // // This function is the same as calling Dqn_Win_NetHandleInit() followed by @@ -354,32 +356,32 @@ enum Dqn_WinNetHandleRequestHeaderFlag struct Dqn_WinNetHandleResponse { - Dqn_String8 raw_headers; - Dqn_String8 *headers; - Dqn_usize headers_size; + Dqn_Str8 raw_headers; + Dqn_Str8 *headers; + Dqn_usize headers_size; // NOTE: Headers pulled from the 'raw_headers' for convenience - uint64_t content_length; - Dqn_String8 content_type; + uint64_t content_length; + Dqn_Str8 content_type; }; -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString (char const *url, int url_size); -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit (Dqn_String8 url); -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCString (char const *url, int url_size, char const *http_method); -DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod (Dqn_String8 url, Dqn_String8 http_method); -DQN_API void Dqn_Win_NetHandleClose (Dqn_WinNetHandle *handle); -DQN_API bool Dqn_Win_NetHandleIsValid (Dqn_WinNetHandle const *handle); -DQN_API void Dqn_Win_NetHandleSetUserAgentCString (Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size); -DQN_API bool Dqn_Win_NetHandleSetHTTPMethod (Dqn_WinNetHandle *handle, char const *method); -DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle, char const *header, int header_size, uint32_t mode); -DQN_API bool Dqn_Win_NetHandleSetRequestHeaderString8 (Dqn_WinNetHandle *handle, Dqn_String8 header, uint32_t mode); -DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest (Dqn_WinNetHandle *handle, Dqn_Allocator allocator, char const *post_data, unsigned long post_data_size); -DQN_API bool Dqn_Win_NetHandlePump (Dqn_WinNetHandle *handle, char *dest, int dest_size, size_t *download_size); -DQN_API char * Dqn_Win_NetHandlePumpCString8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size); -DQN_API Dqn_String8 Dqn_Win_NetHandlePumpString8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena); -DQN_API void Dqn_Win_NetHandlePumpToCRTFile (Dqn_WinNetHandle *handle, FILE *file); -DQN_API char * Dqn_Win_NetHandlePumpToAllocCString (Dqn_WinNetHandle *handle, size_t *download_size); -DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString (Dqn_WinNetHandle *handle); +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCStr8 (char const *url, int url_size); +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit (Dqn_Str8 url); +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCStr8 (char const *url, int url_size, char const *http_method); +DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod (Dqn_Str8 url, Dqn_Str8 http_method); +DQN_API void Dqn_Win_NetHandleClose (Dqn_WinNetHandle *handle); +DQN_API bool Dqn_Win_NetHandleIsValid (Dqn_WinNetHandle const *handle); +DQN_API void Dqn_Win_NetHandleSetUserAgentCStr8 (Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size); +DQN_API bool Dqn_Win_NetHandleSetHTTPMethod (Dqn_WinNetHandle *handle, char const *method); +DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCStr8(Dqn_WinNetHandle *handle, char const *header, int header_size, uint32_t mode); +DQN_API bool Dqn_Win_NetHandleSetRequestHeaderStr8 (Dqn_WinNetHandle *handle, Dqn_Str8 header, uint32_t mode); +DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest (Dqn_WinNetHandle *handle, Dqn_Allocator allocator, char const *post_data, unsigned long post_data_size); +DQN_API bool Dqn_Win_NetHandlePump (Dqn_WinNetHandle *handle, char *dest, int dest_size, size_t *download_size); +DQN_API char * Dqn_Win_NetHandlePumpCStr8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size); +DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpStr8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena); +DQN_API void Dqn_Win_NetHandlePumpToCRTFile (Dqn_WinNetHandle *handle, FILE *file); +DQN_API char * Dqn_Win_NetHandlePumpToAllocCStr8 (Dqn_WinNetHandle *handle, size_t *download_size); +DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpToAllocStr8 (Dqn_WinNetHandle *handle); #endif // !defined(DQN_NO_WINNET) #endif // defined(DQN_OS_WIN32) @@ -405,6 +407,9 @@ DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString (Dqn_W // @param duration_ms_to_gauge_tsc_frequency How many milliseconds to spend // measuring the TSC rate of the current machine. 100ms is sufficient to // produce a fairly accurate result with minimal blocking in applications. +// +// This may return 0 if querying the CPU timestamp counter is not supported +// on the platform (e.g. __rdtsc() or __builtin_readcyclecounter() returns 0). /// Record time between two time-points using the OS's performance counter. struct Dqn_OSTimer @@ -415,7 +420,8 @@ struct Dqn_OSTimer DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size); #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32) -DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Arena* arena); +DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena); +DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena* arena); #endif DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds); DQN_API uint64_t Dqn_OS_PerfCounterNow (); diff --git a/dqn_strings.cpp b/dqn_strings.cpp index bde70b0..ffaa61a 100644 --- a/dqn_strings.cpp +++ b/dqn_strings.cpp @@ -1,5 +1,5 @@ -// NOTE: [$CSTR] Dqn_CString8 ====================================================================== -DQN_API Dqn_usize Dqn_CString8_FSize(DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +// NOTE: [$CSTR] Dqn_CStr8 ====================================================================== +DQN_API Dqn_usize Dqn_CStr8_FSize(DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); @@ -8,7 +8,7 @@ DQN_API Dqn_usize Dqn_CString8_FSize(DQN_FMT_STRING_ANNOTATE char const *fmt, .. return result; } -DQN_API Dqn_usize Dqn_CString8_FVSize(DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API Dqn_usize Dqn_CStr8_FVSize(DQN_FMT_ATTRIB char const *fmt, va_list args) { va_list args_copy; va_copy(args_copy, args); @@ -17,8 +17,7 @@ DQN_API Dqn_usize Dqn_CString8_FVSize(DQN_FMT_STRING_ANNOTATE char const *fmt, v return result; } - -DQN_API Dqn_usize Dqn_CString8_Size(char const *src) +DQN_API Dqn_usize Dqn_CStr8_Size(char const *src) { Dqn_usize result = 0; while (src && src[0] != 0) { @@ -28,7 +27,7 @@ DQN_API Dqn_usize Dqn_CString8_Size(char const *src) return result; } -DQN_API Dqn_usize Dqn_CString16_Size(wchar_t const *src) +DQN_API Dqn_usize Dqn_CStr16_Size(wchar_t const *src) { Dqn_usize result = 0; while (src && src[0] != 0) { @@ -39,28 +38,28 @@ DQN_API Dqn_usize Dqn_CString16_Size(wchar_t const *src) return result; } -// NOTE: [$STR8] Dqn_String8 ======================================================================= -DQN_API Dqn_String8 Dqn_String8_InitCString8(char const *src) +// NOTE: [$STR8] Dqn_Str8 ======================================================================= +DQN_API Dqn_Str8 Dqn_Str8_InitCStr8(char const *src) { - Dqn_usize size = Dqn_CString8_Size(src); - Dqn_String8 result = Dqn_String8_Init(src, size); + Dqn_usize size = Dqn_CStr8_Size(src); + Dqn_Str8 result = Dqn_Str8_Init(src, size); return result; } -DQN_API bool Dqn_String8_IsAll(Dqn_String8 string, Dqn_String8IsAll is_all) +DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all) { - bool result = Dqn_String8_IsValid(string); + bool result = Dqn_Str8_IsValid(string); if (!result) return result; switch (is_all) { - case Dqn_String8IsAll_Digits: { + case Dqn_Str8IsAll_Digits: { for (Dqn_usize index = 0; result && index < string.size; index++) result = string.data[index] >= '0' && string.data[index] <= '9'; } break; - case Dqn_String8IsAll_Hex: { - Dqn_String8 trimmed = Dqn_String8_TrimPrefix(string, DQN_STRING8("0x"), Dqn_String8EqCase_Insensitive); + case Dqn_Str8IsAll_Hex: { + Dqn_Str8 trimmed = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive); for (Dqn_usize index = 0; result && index < string.size; index++) { char ch = trimmed.data[index]; result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); @@ -71,37 +70,37 @@ DQN_API bool Dqn_String8_IsAll(Dqn_String8 string, Dqn_String8IsAll is_all) return result; } -DQN_API Dqn_String8 Dqn_String8_Slice(Dqn_String8 string, Dqn_usize offset, Dqn_usize size) +DQN_API Dqn_Str8 Dqn_Str8_Slice(Dqn_Str8 string, Dqn_usize offset, Dqn_usize size) { - Dqn_String8 result = Dqn_String8_Init(string.data, 0); - if (!Dqn_String8_IsValid(result)) + Dqn_Str8 result = Dqn_Str8_Init(string.data, 0); + if (!Dqn_Str8_IsValid(result)) return result; Dqn_usize capped_offset = DQN_MIN(offset, string.size); Dqn_usize max_size = string.size - capped_offset; Dqn_usize capped_size = DQN_MIN(size, max_size); - result = Dqn_String8_Init(string.data + capped_offset, capped_size); + result = Dqn_Str8_Init(string.data + capped_offset, capped_size); return result; } -DQN_API Dqn_String8 Dqn_String8_Advance(Dqn_String8 string, Dqn_usize amount) +DQN_API Dqn_Str8 Dqn_Str8_Advance(Dqn_Str8 string, Dqn_usize amount) { - Dqn_String8 result = Dqn_String8_Slice(string, amount, UINT64_MAX); + Dqn_Str8 result = Dqn_Str8_Slice(string, amount, DQN_USIZE_MAX); return result; } -DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray(Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) { - Dqn_String8BinarySplitResult result = {}; - if (!Dqn_String8_IsValid(string) || !find || find_size == 0) + Dqn_Str8BinarySplitResult result = {}; + if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) return result; result.lhs = string; - for (size_t index = 0; !result.rhs.data && index <= string.size; index++) { + for (size_t index = 0; !result.rhs.data && index < string.size; index++) { for (Dqn_usize find_index = 0; find_index < find_size; find_index++) { - Dqn_String8 find_item = find[find_index]; - Dqn_String8 string_slice = Dqn_String8_Slice(string, index, find_item.size); - if (Dqn_String8_Eq(string_slice, find_item)) { + Dqn_Str8 find_item = find[find_index]; + Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size); + if (Dqn_Str8_Eq(string_slice, find_item)) { result.lhs.size = index; result.rhs.data = string_slice.data + find_item.size; result.rhs.size = string.size - (index + find_item.size); @@ -113,72 +112,92 @@ DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray(Dqn_String8 st return result; } -DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplit(Dqn_String8 string, Dqn_String8 find) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8 find) { - Dqn_String8BinarySplitResult result = Dqn_String8_BinarySplitArray(string, &find, 1); + Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitArray(string, &find, 1); return result; } -DQN_API Dqn_usize Dqn_String8_Split(Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) { - Dqn_usize result = 0; // The number of splits in the actual string. - if (!Dqn_String8_IsValid(string) || !Dqn_String8_IsValid(delimiter) || delimiter.size <= 0) + Dqn_Str8BinarySplitResult result = {}; + if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) return result; - Dqn_usize splits_index = 0; // The number of splits written. - Dqn_usize begin = 0; - for (Dqn_usize index = 0; index < string.size; ) { - // NOTE: Check if we encountered the substring that is the delimiter - Dqn_String8 check = Dqn_String8_Slice(string, index, delimiter.size); - if (!Dqn_String8_Eq(check, delimiter)) { - index++; - continue; + result.lhs = string; + for (size_t index = string.size - 1; !result.rhs.data && index < string.size; index--) { + for (Dqn_usize find_index = 0; find_index < find_size; find_index++) { + Dqn_Str8 find_item = find[find_index]; + Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size); + if (Dqn_Str8_Eq(string_slice, find_item)) { + result.lhs.size = index; + result.rhs.data = string_slice.data + find_item.size; + result.rhs.size = string.size - (index + find_item.size); + break; + } } - - // NOTE: Generate the split - Dqn_String8 split = Dqn_String8_Init(string.data + begin, index - begin); - if (splits && splits_index < splits_count && split.size) - splits[splits_index++] = split; - - // NOTE: Advance the iterators - result += (split.size > 0); - index += delimiter.size; - begin = index; } return result; } -DQN_API Dqn_String8SplitAllocResult Dqn_String8_SplitAlloc(Dqn_Allocator allocator, - Dqn_String8 string, - Dqn_String8 delimiter) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, Dqn_Str8 find) { - Dqn_String8SplitAllocResult result = {}; - Dqn_usize splits_required = Dqn_String8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0); - result.data = Dqn_Allocator_NewArray(allocator, Dqn_String8, splits_required, Dqn_ZeroMem_No); + Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitReverseArray(string, &find, 1); + return result; +} + +DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count) +{ + Dqn_usize result = 0; // The number of splits in the actual string. + if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(delimiter) || delimiter.size <= 0) + return result; + + Dqn_Str8BinarySplitResult split = {}; + Dqn_Str8 first = string; + do { + split = Dqn_Str8_BinarySplit(first, delimiter); + if (split.lhs.size) { + if (splits && result < splits_count) + splits[result] = split.lhs; + result++; + } + first = split.rhs; + } while (first.size); + + return result; +} + +DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator, + Dqn_Str8 string, + Dqn_Str8 delimiter) +{ + Dqn_Str8SplitAllocResult result = {}; + Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0); + result.data = Dqn_Allocator_NewArray(allocator, Dqn_Str8, splits_required, Dqn_ZeroMem_No); if (result.data) { - result.size = Dqn_String8_Split(string, delimiter, result.data, splits_required); + result.size = Dqn_Str8_Split(string, delimiter, result.data, splits_required); DQN_ASSERT(splits_required == result.size); } return result; } -DQN_API Dqn_String8FindResult Dqn_String8_FindFirstStringArray(Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size) +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) { - Dqn_String8FindResult result = {}; - if (!Dqn_String8_IsValid(string) || !find || find_size == 0) + Dqn_Str8FindResult result = {}; + if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) return result; for (Dqn_usize index = 0; !result.found && index < string.size; index++) { for (Dqn_usize find_index = 0; find_index < find_size; find_index++) { - Dqn_String8 find_item = find[find_index]; - Dqn_String8 string_slice = Dqn_String8_Slice(string, index, find_item.size); - if (Dqn_String8_Eq(string_slice, find_item)) { + Dqn_Str8 find_item = find[find_index]; + Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size); + if (Dqn_Str8_Eq(string_slice, find_item)) { result.found = true; 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_to_end_of_buffer = Dqn_String8_Init(result.match.data, string.size - index); + result.start_to_before_match = Dqn_Str8_Init(string.data, index); + result.match = Dqn_Str8_Init(string.data + index, find_item.size); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index); break; } } @@ -186,37 +205,37 @@ DQN_API Dqn_String8FindResult Dqn_String8_FindFirstStringArray(Dqn_String8 strin return result; } -DQN_API Dqn_String8FindResult Dqn_String8_FindFirstString(Dqn_String8 string, Dqn_String8 find) +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString(Dqn_Str8 string, Dqn_Str8 find) { - Dqn_String8FindResult result = Dqn_String8_FindFirstStringArray(string, &find, 1); + Dqn_Str8FindResult result = Dqn_Str8_FindFirstStringArray(string, &find, 1); return result; } -DQN_API Dqn_String8FindResult Dqn_String8_FindFirst(Dqn_String8 string, uint32_t flags) +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst(Dqn_Str8 string, uint32_t flags) { - Dqn_String8FindResult result = {}; + Dqn_Str8FindResult result = {}; for (size_t index = 0; !result.found && index < string.size; index++) { - result.found |= ((flags & Dqn_String8FindFlag_Digit) && Dqn_Char_IsDigit(string.data[index])); - result.found |= ((flags & Dqn_String8FindFlag_Alphabet) && Dqn_Char_IsAlphabet(string.data[index])); - result.found |= ((flags & Dqn_String8FindFlag_Whitespace) && Dqn_Char_IsWhitespace(string.data[index])); - result.found |= ((flags & Dqn_String8FindFlag_Plus) && string.data[index] == '+'); - result.found |= ((flags & Dqn_String8FindFlag_Minus) && string.data[index] == '-'); + result.found |= ((flags & Dqn_Str8FindFlag_Digit) && Dqn_Char_IsDigit(string.data[index])); + result.found |= ((flags & Dqn_Str8FindFlag_Alphabet) && Dqn_Char_IsAlphabet(string.data[index])); + result.found |= ((flags & Dqn_Str8FindFlag_Whitespace) && Dqn_Char_IsWhitespace(string.data[index])); + result.found |= ((flags & Dqn_Str8FindFlag_Plus) && string.data[index] == '+'); + result.found |= ((flags & Dqn_Str8FindFlag_Minus) && string.data[index] == '-'); if (result.found) { result.index = index; - result.match = Dqn_String8_Init(string.data + index, 1); - result.match_to_end_of_buffer = Dqn_String8_Init(result.match.data, string.size - index); + result.match = Dqn_Str8_Init(string.data + index, 1); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index); } } return result; } -DQN_API Dqn_String8 Dqn_String8_Segment(Dqn_Allocator allocator, Dqn_String8 src, Dqn_usize segment_size, char segment_char) +DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usize segment_size, char segment_char) { Dqn_usize result_size = src.size; if (result_size > segment_size) result_size += (src.size / segment_size) - 1; // NOTE: No segment on the first chunk. - Dqn_String8 result = Dqn_String8_Allocate(allocator, result_size, Dqn_ZeroMem_Yes); + Dqn_Str8 result = Dqn_Str8_Allocate(allocator, result_size, Dqn_ZeroMem_Yes); Dqn_usize write_index = 0; DQN_FOR_UINDEX(src_index, src.size) { result.data[write_index++] = src.data[src_index]; @@ -230,7 +249,7 @@ DQN_API Dqn_String8 Dqn_String8_Segment(Dqn_Allocator allocator, Dqn_String8 src } -DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case) +DQN_API bool Dqn_Str8_Eq(Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case) { if (lhs.size != rhs.size) return false; @@ -243,11 +262,11 @@ DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase bool result = true; switch (eq_case) { - case Dqn_String8EqCase_Sensitive: { + case Dqn_Str8EqCase_Sensitive: { result = (DQN_MEMCMP(lhs.data, rhs.data, lhs.size) == 0); } break; - case Dqn_String8EqCase_Insensitive: { + case Dqn_Str8EqCase_Insensitive: { for (Dqn_usize index = 0; index < lhs.size && result; index++) result = (Dqn_Char_ToLower(lhs.data[index]) == Dqn_Char_ToLower(rhs.data[index])); } break; @@ -255,39 +274,39 @@ DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase return result; } -DQN_API bool Dqn_String8_EqInsensitive(Dqn_String8 lhs, Dqn_String8 rhs) +DQN_API bool Dqn_Str8_EqInsensitive(Dqn_Str8 lhs, Dqn_Str8 rhs) { - bool result = Dqn_String8_Eq(lhs, rhs, Dqn_String8EqCase_Insensitive); + bool result = Dqn_Str8_Eq(lhs, rhs, Dqn_Str8EqCase_Insensitive); return result; } -DQN_API bool Dqn_String8_StartsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case) +DQN_API bool Dqn_Str8_StartsWith(Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case) { - Dqn_String8 substring = {string.data, DQN_MIN(prefix.size, string.size)}; - bool result = Dqn_String8_Eq(substring, prefix, eq_case); + Dqn_Str8 substring = {string.data, DQN_MIN(prefix.size, string.size)}; + bool result = Dqn_Str8_Eq(substring, prefix, eq_case); return result; } -DQN_API bool Dqn_String8_StartsWithInsensitive(Dqn_String8 string, Dqn_String8 prefix) +DQN_API bool Dqn_Str8_StartsWithInsensitive(Dqn_Str8 string, Dqn_Str8 prefix) { - bool result = Dqn_String8_StartsWith(string, prefix, Dqn_String8EqCase_Insensitive); + bool result = Dqn_Str8_StartsWith(string, prefix, Dqn_Str8EqCase_Insensitive); return result; } -DQN_API bool Dqn_String8_EndsWith(Dqn_String8 string, Dqn_String8 suffix, Dqn_String8EqCase eq_case) +DQN_API bool Dqn_Str8_EndsWith(Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case) { - Dqn_String8 substring = {string.data + string.size - suffix.size, DQN_MIN(string.size, suffix.size)}; - bool result = Dqn_String8_Eq(substring, suffix, eq_case); + Dqn_Str8 substring = {string.data + string.size - suffix.size, DQN_MIN(string.size, suffix.size)}; + bool result = Dqn_Str8_Eq(substring, suffix, eq_case); return result; } -DQN_API bool Dqn_String8_EndsWithInsensitive(Dqn_String8 string, Dqn_String8 suffix) +DQN_API bool Dqn_Str8_EndsWithInsensitive(Dqn_Str8 string, Dqn_Str8 suffix) { - bool result = Dqn_String8_EndsWith(string, suffix, Dqn_String8EqCase_Insensitive); + bool result = Dqn_Str8_EndsWith(string, suffix, Dqn_Str8EqCase_Insensitive); return result; } -DQN_API bool Dqn_String8_HasChar(Dqn_String8 string, char ch) +DQN_API bool Dqn_Str8_HasChar(Dqn_Str8 string, char ch) { bool result = false; for (Dqn_usize index = 0; !result && index < string.size; index++) @@ -295,28 +314,35 @@ DQN_API bool Dqn_String8_HasChar(Dqn_String8 string, char ch) return result; } -DQN_API Dqn_String8 Dqn_String8_TrimPrefix(Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case) +DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix(Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case) { - Dqn_String8 result = string; - if (Dqn_String8_StartsWith(string, prefix, eq_case)) { + Dqn_Str8 result = string; + if (Dqn_Str8_StartsWith(string, prefix, eq_case)) { result.data += prefix.size; result.size -= prefix.size; } return result; } -DQN_API Dqn_String8 Dqn_String8_TrimSuffix(Dqn_String8 string, Dqn_String8 suffix, Dqn_String8EqCase eq_case) +DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix(Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case) { - Dqn_String8 result = string; - if (Dqn_String8_EndsWith(string, suffix, eq_case)) + Dqn_Str8 result = string; + if (Dqn_Str8_EndsWith(string, suffix, eq_case)) result.size -= suffix.size; return result; } -DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround(Dqn_String8 string) +DQN_API Dqn_Str8 Dqn_Str8_TrimAround(Dqn_Str8 string, Dqn_Str8 trim_string) { - Dqn_String8 result = string; - if (!Dqn_String8_IsValid(string)) + Dqn_Str8 result = Dqn_Str8_TrimPrefix(string, trim_string); + result = Dqn_Str8_TrimSuffix(result, trim_string); + return result; +} + +DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string) +{ + Dqn_Str8 result = string; + if (!Dqn_Str8_IsValid(string)) return result; char const *start = string.data; @@ -327,60 +353,62 @@ DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround(Dqn_String8 string) while (end > start && Dqn_Char_IsWhitespace(end[-1])) end--; - result = Dqn_String8_Init(start, end - start); + result = Dqn_Str8_Init(start, end - start); return result; } -DQN_API Dqn_String8 Dqn_String8_TrimByteOrderMark(Dqn_String8 string) +DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string) { - Dqn_String8 result = string; - if (!Dqn_String8_IsValid(result)) + Dqn_Str8 result = string; + if (!Dqn_Str8_IsValid(result)) return result; // TODO(dqn): This is little endian - Dqn_String8 UTF8_BOM = DQN_STRING8("\xEF\xBB\xBF"); - Dqn_String8 UTF16_BOM_BE = DQN_STRING8("\xEF\xFF"); - Dqn_String8 UTF16_BOM_LE = DQN_STRING8("\xFF\xEF"); - Dqn_String8 UTF32_BOM_BE = DQN_STRING8("\x00\x00\xFE\xFF"); - Dqn_String8 UTF32_BOM_LE = DQN_STRING8("\xFF\xFE\x00\x00"); + Dqn_Str8 UTF8_BOM = DQN_STR8("\xEF\xBB\xBF"); + Dqn_Str8 UTF16_BOM_BE = DQN_STR8("\xEF\xFF"); + Dqn_Str8 UTF16_BOM_LE = DQN_STR8("\xFF\xEF"); + Dqn_Str8 UTF32_BOM_BE = DQN_STR8("\x00\x00\xFE\xFF"); + Dqn_Str8 UTF32_BOM_LE = DQN_STR8("\xFF\xFE\x00\x00"); - result = Dqn_String8_TrimPrefix(result, UTF8_BOM, Dqn_String8EqCase_Sensitive); - result = Dqn_String8_TrimPrefix(result, UTF16_BOM_BE, Dqn_String8EqCase_Sensitive); - result = Dqn_String8_TrimPrefix(result, UTF16_BOM_LE, Dqn_String8EqCase_Sensitive); - result = Dqn_String8_TrimPrefix(result, UTF32_BOM_BE, Dqn_String8EqCase_Sensitive); - result = Dqn_String8_TrimPrefix(result, UTF32_BOM_LE, Dqn_String8EqCase_Sensitive); + result = Dqn_Str8_TrimPrefix(result, UTF8_BOM, Dqn_Str8EqCase_Sensitive); + result = Dqn_Str8_TrimPrefix(result, UTF16_BOM_BE, Dqn_Str8EqCase_Sensitive); + result = Dqn_Str8_TrimPrefix(result, UTF16_BOM_LE, Dqn_Str8EqCase_Sensitive); + result = Dqn_Str8_TrimPrefix(result, UTF32_BOM_BE, Dqn_Str8EqCase_Sensitive); + result = Dqn_Str8_TrimPrefix(result, UTF32_BOM_LE, Dqn_Str8EqCase_Sensitive); return result; } -DQN_API Dqn_String8 Dqn_String8_FileNameFromPath(Dqn_String8 path) +DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path) { - Dqn_String8 result = path; - if (!Dqn_String8_IsValid(result)) - return result; - - DQN_MSVC_WARNING_PUSH - DQN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop. - for (Dqn_usize index = result.size - 1; index < result.size; --index) { - if (result.data[index] == '\\' || result.data[index] == '/') { - char const *end = result.data + result.size; - result.data = result.data + (index + 1); - result.size = end - result.data; - break; - } - } - DQN_MSVC_WARNING_POP + Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators)); + Dqn_Str8 result = split.rhs; return result; } -DQN_API Dqn_String8ToU64Result Dqn_String8_ToU64(Dqn_String8 string, char separator) +DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension(Dqn_Str8 path) +{ + Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(path); + Dqn_Str8 result = Dqn_Str8_FilePathNoExtension(file_name); + return result; +} + +DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path) +{ + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8(".")); + Dqn_Str8 result = split.lhs; + return result; +} + +DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64(Dqn_Str8 string, char separator) { // NOTE: Argument check - Dqn_String8ToU64Result result = {}; - if (!Dqn_String8_IsValid(string)) + Dqn_Str8ToU64Result result = {}; + if (!Dqn_Str8_IsValid(string)) return result; // NOTE: Sanitize input/output - Dqn_String8 trim_string = Dqn_String8_TrimWhitespaceAround(string); + Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string); if (trim_string.size == 0) { result.success = false; return result; @@ -414,15 +442,15 @@ DQN_API Dqn_String8ToU64Result Dqn_String8_ToU64(Dqn_String8 string, char separa return result; } -DQN_API Dqn_String8ToI64Result Dqn_String8_ToI64(Dqn_String8 string, char separator) +DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64(Dqn_Str8 string, char separator) { // NOTE: Argument check - Dqn_String8ToI64Result result = {}; - if (!Dqn_String8_IsValid(string)) + Dqn_Str8ToI64Result result = {}; + if (!Dqn_Str8_IsValid(string)) return result; // NOTE: Sanitize input/output - Dqn_String8 trim_string = Dqn_String8_TrimWhitespaceAround(string); + Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string); if (trim_string.size == 0) { result.success = false; return result; @@ -460,28 +488,28 @@ DQN_API Dqn_String8ToI64Result Dqn_String8_ToI64(Dqn_String8 string, char separa return result; } -DQN_API Dqn_String8 Dqn_String8_Replace(Dqn_String8 string, - Dqn_String8 find, - Dqn_String8 replace, - Dqn_usize start_index, - Dqn_Allocator allocator, - Dqn_String8EqCase eq_case) +DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string, + Dqn_Str8 find, + Dqn_Str8 replace, + Dqn_usize start_index, + Dqn_Allocator allocator, + Dqn_Str8EqCase eq_case) { - Dqn_String8 result = {}; - if (!Dqn_String8_IsValid(string) || !Dqn_String8_IsValid(find) || find.size > string.size || find.size == 0 || string.size == 0) { - result = Dqn_String8_Copy(allocator, string); + Dqn_Str8 result = {}; + if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(find) || find.size > string.size || find.size == 0 || string.size == 0) { + result = Dqn_Str8_Copy(allocator, string); return result; } - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); - Dqn_String8Builder string_builder = {}; - string_builder.allocator = scratch.allocator; - Dqn_usize max = string.size - find.size; - Dqn_usize head = start_index; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); + Dqn_Str8Builder string_builder = {}; + string_builder.allocator = scratch.allocator; + Dqn_usize max = string.size - find.size; + Dqn_usize head = start_index; for (Dqn_usize tail = head; tail <= max; tail++) { - Dqn_String8 check = Dqn_String8_Slice(string, tail, find.size); - if (!Dqn_String8_Eq(check, find, eq_case)) + Dqn_Str8 check = Dqn_Str8_Slice(string, tail, find.size); + if (!Dqn_Str8_Eq(check, find, eq_case)) continue; if (start_index > 0 && string_builder.string_size == 0) { @@ -489,38 +517,38 @@ DQN_API Dqn_String8 Dqn_String8_Replace(Dqn_String8 string, // need to add the string up to the hint. We only do this if there's // a replacement action, otherwise we have a special case for no // replacements, where the entire string gets copied. - Dqn_String8 slice = Dqn_String8_Init(string.data, head); - Dqn_String8Builder_AppendRef(&string_builder, slice); + Dqn_Str8 slice = Dqn_Str8_Init(string.data, head); + Dqn_Str8Builder_AppendRef(&string_builder, slice); } - Dqn_String8 range = Dqn_String8_Slice(string, head, (tail - head)); - Dqn_String8Builder_AppendRef(&string_builder, range); - Dqn_String8Builder_AppendRef(&string_builder, replace); + Dqn_Str8 range = Dqn_Str8_Slice(string, head, (tail - head)); + Dqn_Str8Builder_AppendRef(&string_builder, range); + Dqn_Str8Builder_AppendRef(&string_builder, replace); head = tail + find.size; tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string } if (string_builder.string_size == 0) { // NOTE: No replacement possible, so we just do a full-copy - result = Dqn_String8_Copy(allocator, string); + result = Dqn_Str8_Copy(allocator, string); } else { - Dqn_String8 remainder = Dqn_String8_Init(string.data + head, string.size - head); - Dqn_String8Builder_AppendRef(&string_builder, remainder); - result = Dqn_String8Builder_Build(&string_builder, allocator); + Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head); + Dqn_Str8Builder_AppendRef(&string_builder, remainder); + result = Dqn_Str8Builder_Build(&string_builder, allocator); } return result; } -DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive(Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator) +DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator) { - Dqn_String8 result = Dqn_String8_Replace(string, find, replace, start_index, allocator, Dqn_String8EqCase_Insensitive); + Dqn_Str8 result = Dqn_Str8_Replace(string, find, replace, start_index, allocator, Dqn_Str8EqCase_Insensitive); return result; } -DQN_API void Dqn_String8_Remove(Dqn_String8 *string, Dqn_usize offset, Dqn_usize size) +DQN_API void Dqn_Str8_Remove(Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size) { - if (!string || !Dqn_String8_IsValid(*string)) + if (!string || !Dqn_Str8_IsValid(*string)) return; char *end = string->data + string->size; @@ -532,79 +560,79 @@ DQN_API void Dqn_String8_Remove(Dqn_String8 *string, Dqn_usize offset, Dqn_usize } #if defined(__cplusplus) -DQN_API bool operator==(Dqn_String8 const &lhs, Dqn_String8 const &rhs) +DQN_API bool operator==(Dqn_Str8 const &lhs, Dqn_Str8 const &rhs) { - bool result = Dqn_String8_Eq(lhs, rhs, Dqn_String8EqCase_Sensitive); + bool result = Dqn_Str8_Eq(lhs, rhs, Dqn_Str8EqCase_Sensitive); return result; } -DQN_API bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs) +DQN_API bool operator!=(Dqn_Str8 const &lhs, Dqn_Str8 const &rhs) { bool result = !(lhs == rhs); return result; } #endif -DQN_API Dqn_String8 Dqn_String8_InitF(Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API Dqn_Str8 Dqn_Str8_InitF(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, ...) { va_list va; va_start(va, fmt); - Dqn_String8 result = Dqn_String8_InitFV(allocator, fmt, va); + Dqn_Str8 result = Dqn_Str8_InitFV(allocator, fmt, va); va_end(va); return result; } -DQN_API Dqn_String8 Dqn_String8_InitFV(Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, va_list args) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; if (!fmt) return result; - Dqn_usize size = Dqn_CString8_FVSize(fmt, args); + Dqn_usize size = Dqn_CStr8_FVSize(fmt, args); if (size) { - result = Dqn_String8_Allocate(allocator, size, Dqn_ZeroMem_No); - if (Dqn_String8_IsValid(result)) + result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No); + if (Dqn_Str8_IsValid(result)) STB_SPRINTF_DECORATE(vsnprintf)(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args); } return result; } -DQN_API Dqn_String8 Dqn_String8_Allocate(Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem) +DQN_API Dqn_Str8 Dqn_Str8_Allocate(Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem) { - Dqn_String8 result = {}; - result.data = (char *)Dqn_Allocator_Alloc(allocator, size + 1, alignof(char), zero_mem); + Dqn_Str8 result = {}; + result.data = (char *)Dqn_Allocator_Alloc(allocator, size + 1, alignof(char), zero_mem); if (result.data) result.size = size; return result; } -DQN_API Dqn_String8 Dqn_String8_CopyCString(Dqn_Allocator allocator, char const *string, Dqn_usize size) +DQN_API Dqn_Str8 Dqn_Str8_CopyCString(Dqn_Allocator allocator, char const *string, Dqn_usize size) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; if (!string) return result; - result = Dqn_String8_Allocate(allocator, size, Dqn_ZeroMem_No); - if (Dqn_String8_IsValid(result)) { + result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No); + if (Dqn_Str8_IsValid(result)) { DQN_MEMCPY(result.data, string, size); result.data[size] = 0; } return result; } -DQN_API Dqn_String8 Dqn_String8_Copy(Dqn_Allocator allocator, Dqn_String8 string) +DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Allocator allocator, Dqn_Str8 string) { - Dqn_String8 result = Dqn_String8_CopyCString(allocator, string.data, string.size); + Dqn_Str8 result = Dqn_Str8_CopyCString(allocator, string.data, string.size); return result; } -// NOTE: [$STRB] Dqn_String8Builder ================================================================ -DQN_API bool Dqn_String8Builder_AppendRef(Dqn_String8Builder *builder, Dqn_String8 string) +// NOTE: [$STRB] Dqn_Str8Builder ================================================================ +DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string) { if (!builder || !string.data || string.size <= 0) return false; - Dqn_String8Link *link = Dqn_Allocator_New(builder->allocator, Dqn_String8Link, Dqn_ZeroMem_No); + Dqn_Str8Link *link = Dqn_Allocator_New(builder->allocator, Dqn_Str8Link, Dqn_ZeroMem_No); if (!link) return false; @@ -622,37 +650,37 @@ DQN_API bool Dqn_String8Builder_AppendRef(Dqn_String8Builder *builder, Dqn_Strin return true; } -DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_String8 string) +DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string) { - Dqn_String8 copy = Dqn_String8_Copy(builder->allocator, string); - bool result = Dqn_String8Builder_AppendRef(builder, copy); + Dqn_Str8 copy = Dqn_Str8_Copy(builder->allocator, string); + bool result = Dqn_Str8Builder_AppendRef(builder, copy); return result; } -DQN_API bool Dqn_String8Builder_AppendFV(Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args) { - Dqn_String8 string = Dqn_String8_InitFV(builder->allocator, fmt, args); + Dqn_Str8 string = Dqn_Str8_InitFV(builder->allocator, fmt, args); if (string.size == 0) return true; - bool result = Dqn_String8Builder_AppendRef(builder, string); + bool result = Dqn_Str8Builder_AppendRef(builder, string); if (!result) Dqn_Allocator_Dealloc(builder->allocator, string.data, string.size + 1); return result; } -DQN_API bool Dqn_String8Builder_AppendF(Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...) { va_list args; va_start(args, fmt); - bool result = Dqn_String8Builder_AppendFV(builder, fmt, args); + bool result = Dqn_Str8Builder_AppendFV(builder, fmt, args); va_end(args); return result; } -DQN_API Dqn_String8 Dqn_String8Builder_Build(Dqn_String8Builder const *builder, Dqn_Allocator allocator) +DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Allocator allocator) { - Dqn_String8 result = DQN_ZERO_INIT; + Dqn_Str8 result = DQN_ZERO_INIT; if (!builder || builder->string_size <= 0 || builder->count <= 0) return result; @@ -660,7 +688,7 @@ DQN_API Dqn_String8 Dqn_String8Builder_Build(Dqn_String8Builder const *builder, if (!result.data) return result; - for (Dqn_String8Link *link = builder->head; link; link = link->next) { + for (Dqn_Str8Link *link = builder->head; link; link = link->next) { DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size); result.size += link->string.size; } @@ -810,4 +838,3 @@ DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint) return 0; } - diff --git a/dqn_strings.h b/dqn_strings.h index 8c2d971..d8f9111 100644 --- a/dqn_strings.h +++ b/dqn_strings.h @@ -1,57 +1,57 @@ -// NOTE: [$CSTR] Dqn_CString8 ====================================================================== -// @proc Dqn_CString8_ArrayCount +// NOTE: [$CSTR] Dqn_CStr8 ====================================================================== +// @proc Dqn_CStr8_ArrayCount // @desc Calculate the size of a cstring literal/array at compile time // @param literal The cstring literal/array to calculate the size for // @return The size of the cstring not including the null-terminating byte -// @proc Dqn_CString8_FSize, Dqn_CString8_FVSize +// @proc Dqn_CStr8_FSize, Dqn_CStr8_FVSize // Calculate the required size to format the given format cstring. // @param[in] fmt The format string to calculate the size for // @return The size required to format the string, not including the null // terminator. -// @proc Dqn_CString8_Size +// @proc Dqn_CStr8_Size // @desc Calculate the string length of the null-terminated string. // @param[in] a The string whose length is to be determined // @return The length of the string -DQN_API template constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; } -DQN_API template constexpr Dqn_usize Dqn_CString8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; } -DQN_API Dqn_usize Dqn_CString8_FSize (DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API Dqn_usize Dqn_CString8_FVSize (DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); -DQN_API Dqn_usize Dqn_CString8_Size (char const *a); -DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a); +DQN_API template constexpr Dqn_usize Dqn_CStr8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; } +DQN_API template constexpr Dqn_usize Dqn_CStr8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; } +DQN_API Dqn_usize Dqn_CStr8_FSize (DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API Dqn_usize Dqn_CStr8_FVSize (DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API Dqn_usize Dqn_CStr8_Size (char const *a); +DQN_API Dqn_usize Dqn_CStr16_Size (wchar_t const *a); -// NOTE: [$STR8] Dqn_String8 ======================================================================= +// NOTE: [$STR8] Dqn_Str8 ======================================================================= // NOTE: API -// @proc Dqn_String8_Init +// @proc Dqn_Str8_Init // @desc Initialise a string from a pointer and length -// The string is invalid (i.e. Dqn_String8_IsValid() returns false) if size is +// The string is invalid (i.e. Dqn_Str8_IsValid() returns false) if size is // negative or the string is null. -// @proc Dqn_String8_InitCString +// @proc Dqn_Str8_InitCString // @desc Initialise a string from a cstring // The cstring must be null-terminated as its length is evaluated using -// strlen. The string is invalid (i.e. Dqn_String8_IsValid() returns false) if +// strlen. The string is invalid (i.e. Dqn_Str8_IsValid() returns false) if // size is negative or the string is null. -// @proc Dqn_String8_InitF +// @proc Dqn_Str8_InitF // @desc Create a string from a printf style format string // @param[in] allocator The allocator the string will be allocated from // @param[in] fmt The printf style format cstring -// @proc Dqn_String8_InitFV +// @proc Dqn_Str8_InitFV // @desc Create a string from a printf style format string using a va_list // @param[in] arena The allocator the string will be allocated from // @param[in] fmt The printf style format cstring // @param[in] va The variable argument list // -// @proc Dqn_String8_IsValid +// @proc Dqn_Str8_IsValid // @desc Determine if the values of the given string are valid // A string is invalid if size is negative or the string is null. // @return True if the string is valid, false otherwise. -// @proc Dqn_String8 Dqn_String8_Slice +// @proc Dqn_Str8 Dqn_Str8_Slice // @desc Create a slice from a pre-existing string. // The requested slice is clamped to within the bounds of the original string. // @param[in] string The string to slice @@ -59,7 +59,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @param[in] size The size of the slice // @return The sliced string -// @proc Dqn_String8_BinarySplit +// @proc Dqn_Str8_BinarySplit // @desc Split a string into the substring occuring prior and after the first // occurence of the `delimiter`. Neither strings include the `delimiter`. // @@ -73,7 +73,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return The left hand side of the split string. The original pointer is // returned if the arguments were invalid. -// @proc Dqn_String8_Split +// @proc Dqn_Str8_Split // @desc Split a string by the delimiting character. // This function can evaluate the number of splits required in the return value // by setting `splits` to null and `splits_count` to 0. @@ -88,31 +88,31 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // capacity given by the caller, i.e. `splits_count`. This function should be // called again with a sufficiently sized array if all splits are desired. -// @proc Dqn_String8_Segment +// @proc Dqn_Str8_Segment // @desc Segment a string by inserting the `segment_char` every `segment_size` // characters in the string. For example, '123456789' split with // `segment_char` ' ' and `segment_size` '3' would yield, '123 456 789'. -// @proc Dqn_String8_Allocate +// @proc Dqn_Str8_Allocate // @desc Create an empty string with the requested size // @param[in] allocator The allocator the string will be allocated from // @param[in] size The size in bytes of the string to allocate // @param[in] zero_mem Enum to indicate if the string's memory should be cleared -// @proc Dqn_String8_CopyCString +// @proc Dqn_Str8_CopyCString // @desc Create a copy of the given cstring // @param[in] allocator The allocator the string will be allocated from // @param[in] string The cstring to copy // @param[in] size The size of the cstring to copy. This cannot be <= 0 // @return A copy of the string, invalid string if any argument was invalid. -// @proc Dqn_String8_Copy +// @proc Dqn_Str8_Copy // @desc Create a copy of the given string // @param[in] allocator The allocator the string will be allocated from // @param[in] string The string to copy // @return A copy of the string, invalid string if any argument was invalid. -// @proc Dqn_String8_Eq, Dqn_String8_EqInsensitive +// @proc Dqn_Str8_Eq, Dqn_Str8_EqInsensitive // @desc Compare a string for equality with or without case sensitivity. // @param[in] lhs The first string to compare equality with // @param[in] rhs The second string to compare equality with @@ -122,8 +122,8 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return True if the arguments are valid, non-null and the strings // are equal, false otherwise. -// @proc Dqn_String8_StartsWith, Dqn_String8_StartsWithInsensitive, -// Dqn_String8_EndsWith, Dqn_String8_EndswithInsensitive +// @proc Dqn_Str8_StartsWith, Dqn_Str8_StartsWithInsensitive, +// Dqn_Str8_EndsWith, Dqn_Str8_EndswithInsensitive // @desc Check if a string starts/ends with the specified prefix // `EndsWithInsensitive` is case insensitive // @param[in] string The string to check for the prefix @@ -132,7 +132,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return True if the string is valid, non-null and has the specified prefix, // false otherwise. -// @proc Dqn_String8_TrimPrefix, Dqn_String8_TrimSuffix +// @proc Dqn_Str8_TrimPrefix, Dqn_Str8_TrimSuffix // @desc Remove the prefix/suffix respectively from the given `string. // // @param[in] string The string to trim @@ -144,7 +144,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return The trimmed string. The original input string is returned if // arguments are invalid or no trim was possible. -// @proc Dqn_String8_TrimWhitespaceAround +// @proc Dqn_Str8_TrimWhitespaceAround // @desc Trim whitespace from the prefix and suffix of the string // // @param[in] string The string to trim @@ -154,7 +154,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return The trimmed string. The original input string is returned if // arguments are invalid or no trim was possible. -// @proc Dqn_String8_TrimByteOrderMark +// @proc Dqn_Str8_TrimByteOrderMark // @desc Trim UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark prefix in the string. // // @param[in] string The string to trim @@ -164,7 +164,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return The trimmed string. The original input string is returned if // arguments are invalid or no trim was possible. -// @proc Dqn_String8_FileNameFromPath +// @proc Dqn_Str8_FileNameFromPath // @desc Get the file name from a file path. The file name is evaluated by // searching from the end of the string backwards to the first occurring path // separator '/' or '\'. If no path separator is found, the original string is @@ -178,7 +178,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @return The file name in the file path, if none is found, the original path // string is returned. Null pointer if arguments are null or invalid. -// @proc Dqn_String8_ToI64, Dqn_String8_ToU64 +// @proc Dqn_Str8_ToI64, Dqn_Str8_ToU64 // @desc Convert a number represented as a string to a signed 64 bit number. // // The `separator` is an optional digit separator for example, if `separator` @@ -197,37 +197,37 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha // @param[in] separator The character used to separate the digits, if any. Set // this to 0, if no separators are permitted. -// @proc Dqn_String8_Replace, Dqn_String8_ReplaceInsensitive +// @proc Dqn_Str8_Replace, Dqn_Str8_ReplaceInsensitive // @desc TODO(doyle): Write description -// @proc Dqn_String8_Remove +// @proc Dqn_Str8_Remove // @desc Remove the substring denoted by the begin index and the size from the string // string in-place using MEMMOVE to shift the string back. -// @proc Dqn_String8_Find +// @proc Dqn_Str8_Find // @desc @param start_index Set an index within the string string to start the search // from, if not desired, set to 0 // @return A string that points to the matching find, otherwise a 0 length string. -// @proc DQN_STRING8 -// @desc Construct a UTF8 c-string literal into a Dqn_String8 referencing a +// @proc DQN_STR8 +// @desc Construct a UTF8 c-string literal into a Dqn_Str8 referencing a // string stored in the data-segment. This string is read-only. -// @proc DQN_STRING16 -// @desc Construct a UTF16 c-string literal into a Dqn_String16 referencing a string +// @proc DQN_STR16 +// @desc Construct a UTF16 c-string literal into a Dqn_Str16 referencing a string // stored in the data-segment. This string is read-only. -// @proc DQN_STRING_FMT +// @proc DQN_STR_FMT // @desc Unpack a string into arguments for printing a string into a printf style // format string. -struct Dqn_String8Link +struct Dqn_Str8Link { - Dqn_String8 string; // The string - Dqn_String8Link *next; // The next string in the linked list + Dqn_Str8 string; // The string + Dqn_Str8Link *next; // The next string in the linked list }; -struct Dqn_String16 /// A pointer and length style string that holds slices to UTF16 bytes. +struct Dqn_Str16 /// A pointer and length style string that holds slices to UTF16 bytes. { wchar_t *data; // The UTF16 bytes of the string Dqn_usize size; // The number of characters in the string @@ -240,150 +240,155 @@ struct Dqn_String16 /// A pointer and length style string that holds slices to U #endif }; -struct Dqn_String8BinarySplitResult +struct Dqn_Str8BinarySplitResult { - Dqn_String8 lhs; - Dqn_String8 rhs; + Dqn_Str8 lhs; + Dqn_Str8 rhs; }; -struct Dqn_String8FindResult +struct Dqn_Str8FindResult { - bool found; // True if string was found. If false, the subsequent fields below are not set. - Dqn_usize index; // The index in the buffer where the found string starts - Dqn_String8 match; // The matching string in the buffer that was searched - 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 + bool found; // True if string was found. If false, the subsequent fields below are not set. + Dqn_usize index; // The index in the buffer where the found string starts + Dqn_Str8 match; // The matching string in the buffer that was searched + Dqn_Str8 match_to_end_of_buffer; // The substring containing the found string to the end of the buffer + Dqn_Str8 start_to_before_match; // The substring from the start of the buffer up until the found string, not including it }; // NOTE: Macros ==================================================================================== -#define DQN_STRING8(string) Dqn_String8{(char *)(string), sizeof(string) - 1} -#define DQN_STRING16(string) Dqn_String16{(wchar_t *)(string), (sizeof(string)/sizeof(string[0])) - 1} -#define DQN_STRING_FMT(string) (int)((string).size), (string).data +#define DQN_STR8(string) Dqn_Str8{(char *)(string), sizeof(string) - 1} +#define DQN_STR16(string) Dqn_Str16{(wchar_t *)(string), (sizeof(string)/sizeof(string[0])) - 1} +#define DQN_STR_FMT(string) (int)((string).size), (string).data #if defined(__cplusplus) -#define Dqn_String8_Init(data, size) (Dqn_String8{(char *)(data), (Dqn_usize)(size)}) +#define Dqn_Str8_Init(data, size) (Dqn_Str8{(char *)(data), (Dqn_usize)(size)}) #else -#define Dqn_String8_Init(data, size) (Dqn_String8){(data), (size)} +#define Dqn_Str8_Init(data, size) (Dqn_Str8){(data), (size)} #endif // NOTE: API ======================================================================================= -enum Dqn_String8IsAll +enum Dqn_Str8IsAll { - Dqn_String8IsAll_Digits, - Dqn_String8IsAll_Hex, + Dqn_Str8IsAll_Digits, + Dqn_Str8IsAll_Hex, }; -enum Dqn_String8EqCase +enum Dqn_Str8EqCase { - Dqn_String8EqCase_Sensitive, - Dqn_String8EqCase_Insensitive, + Dqn_Str8EqCase_Sensitive, + Dqn_Str8EqCase_Insensitive, }; -enum Dqn_String8FindFlag +enum Dqn_Str8FindFlag { - Dqn_String8FindFlag_Digit = 1 << 0, // 0-9 - Dqn_String8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' ' - Dqn_String8FindFlag_Alphabet = 1 << 2, // A-Z, a-z - Dqn_String8FindFlag_Plus = 1 << 3, // + - Dqn_String8FindFlag_Minus = 1 << 4, // - - Dqn_String8FindFlag_AlphaNum = Dqn_String8FindFlag_Alphabet | Dqn_String8FindFlag_Digit, + Dqn_Str8FindFlag_Digit = 1 << 0, // 0-9 + Dqn_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' ' + Dqn_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z + Dqn_Str8FindFlag_Plus = 1 << 3, // + + Dqn_Str8FindFlag_Minus = 1 << 4, // - + Dqn_Str8FindFlag_AlphaNum = Dqn_Str8FindFlag_Alphabet | Dqn_Str8FindFlag_Digit, }; -struct Dqn_String8SplitAllocResult +struct Dqn_Str8SplitAllocResult { - Dqn_String8 *data; + Dqn_Str8 *data; Dqn_usize size; }; -struct Dqn_String8ToU64Result +struct Dqn_Str8ToU64Result { bool success; uint64_t value; }; -struct Dqn_String8ToI64Result +struct Dqn_Str8ToI64Result { bool success; int64_t value; }; -DQN_API Dqn_String8 Dqn_String8_InitCString8 (char const *src); -#define Dqn_String8_IsValid(string) ((string).data) -DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all); +DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src); +#define Dqn_Str8_IsValid(string) ((string).data) +DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all); -DQN_API Dqn_String8 Dqn_String8_InitF (Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API Dqn_String8 Dqn_String8_InitFV (Dqn_Allocator allocator, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); -DQN_API Dqn_String8 Dqn_String8_Allocate (Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem); -DQN_API Dqn_String8 Dqn_String8_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size); -DQN_API Dqn_String8 Dqn_String8_Copy (Dqn_Allocator allocator, Dqn_String8 string); +DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API Dqn_Str8 Dqn_Str8_Allocate (Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem); +DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size); +DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Allocator allocator, Dqn_Str8 string); -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_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_String8SplitAllocResult Dqn_String8_SplitAlloc (Dqn_Allocator allocator, Dqn_String8 string, Dqn_String8 delimiter); +DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size); +DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count); +DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc (Dqn_Allocator allocator, Dqn_Str8 string, Dqn_Str8 delimiter); -DQN_API Dqn_String8FindResult Dqn_String8_FindFirstStringArray (Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size); -DQN_API Dqn_String8FindResult Dqn_String8_FindFirstString (Dqn_String8 string, Dqn_String8 find); -DQN_API Dqn_String8FindResult Dqn_String8_FindFirst (Dqn_String8 string, uint32_t flags); -DQN_API Dqn_String8 Dqn_String8_Segment (Dqn_Allocator allocator, Dqn_String8 src, Dqn_usize segment_size, char segment_char); +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst (Dqn_Str8 string, uint32_t flags); +DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); -DQN_API bool Dqn_String8_Eq (Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API bool Dqn_String8_EqInsensitive (Dqn_String8 lhs, Dqn_String8 rhs); -DQN_API bool Dqn_String8_StartsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API bool Dqn_String8_StartsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix); -DQN_API bool Dqn_String8_EndsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API bool Dqn_String8_EndsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix); -DQN_API bool Dqn_String8_HasChar (Dqn_String8 string, char ch); +DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs); +DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix); +DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix); +DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch); -DQN_API Dqn_String8 Dqn_String8_TrimPrefix (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API Dqn_String8 Dqn_String8_TrimSuffix (Dqn_String8 string, Dqn_String8 suffix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround (Dqn_String8 string); -DQN_API Dqn_String8 Dqn_String8_TrimByteOrderMark (Dqn_String8 string); +DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string); +DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string); +DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string); -DQN_API Dqn_String8 Dqn_String8_FileNameFromPath (Dqn_String8 path); +DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path); +DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path); +DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path); -DQN_API Dqn_String8ToU64Result Dqn_String8_ToU64 (Dqn_String8 string, char separator); -DQN_API Dqn_String8ToI64Result Dqn_String8_ToI64 (Dqn_String8 string, char separator); +DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator); +DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator); -DQN_API Dqn_String8 Dqn_String8_Replace (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); -DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator); -DQN_API void Dqn_String8_Remove (Dqn_String8 *string, Dqn_usize offset, Dqn_usize size); +DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive); +DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator); +DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size); #if defined(__cplusplus) -DQN_API bool operator== (Dqn_String8 const &lhs, Dqn_String8 const &rhs); -DQN_API bool operator!= (Dqn_String8 const &lhs, Dqn_String8 const &rhs); +DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs); +DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs); #endif -#if !defined(DQN_NO_FSTRING8) -// NOTE: [$FSTR] Dqn_FString8 ====================================================================== +#if !defined(DQN_NO_FSTR8) +// NOTE: [$FSTR] Dqn_FStr8 ========================================================================= // NOTE: API ======================================================================================= -// @proc Dqn_FString8_InitF +// @proc Dqn_FStr8_InitF // @desc Create a fixed string from the format string. The result string is // null-terminated. // @param fmt[in] Format string specifier to create the fixed string from // @return The created string, truncated if there was insufficient space -// @proc Dqn_FString8_Max +// @proc Dqn_FStr8_Max // @desc @param string[in] The string to query the maximum capacity of // @return Maximum capacity of the fixed string -// @proc Dqn_FString8_Clear +// @proc Dqn_FStr8_Clear // @desc Reset the characters in the string // @param string[in] The string to clear -// @proc Dqn_FString8_AppendFV +// @proc Dqn_FStr8_AppendFV // @desc Append a format string to the fixed string. On failure the string is // appended to but truncated ensuring null-termination. // @param string[in] The string to append to // @param fmt[in] Format string to append to the fixed string // @return True if append was successful, false otherwise. -// @proc Dqn_FString8_AppendF -// @desc @copydocs Dqn_FString8_AppendF +// @proc Dqn_FStr8_AppendF +// @desc @copydocs Dqn_FStr8_AppendF -// @proc Dqn_FString8_AppendCString8 +// @proc Dqn_FStr8_AppendCStr8 // @desc Append a cstring to the fixed string. On failure the string is // appended to but truncated ensuring null-termination. // @param string[in] The string to append to @@ -391,7 +396,7 @@ DQN_API bool operator!= (Dqn_Stri // @param size[in] Size of the cstring // @return True if append was successful, false otherwise. -// @proc Dqn_FString8_Append +// @proc Dqn_FStr8_Append // @desc Append a string to the fixed string. On failure the string is // appended to but truncated ensuring null-termination. // @param string[in] The string to append to @@ -399,38 +404,38 @@ DQN_API bool operator!= (Dqn_Stri // determined before appending. // @return True if append was successful, false otherwise. -// @proc Dqn_FString8_ToString8 +// @proc Dqn_FStr8_ToStr8 // @desc Convert a fixed string to a string. The string holds a reference to the // fixed string and is invalidated once fixed string is deleted. // @param string[in] The fixed string to create a string from // @return String referencing the contents of `string` -// @proc Dqn_FString8_Eq -// @desc @see Dqn_String8_Eq +// @proc Dqn_FStr8_Eq +// @desc @see Dqn_Str8_Eq -// @proc Dqn_FString8_EqString8 -// @desc @see Dqn_String8_Eq +// @proc Dqn_FStr8_EqStr8 +// @desc @see Dqn_Str8_Eq -// @proc Dqn_FString8_EqInsensitive +// @proc Dqn_FStr8_EqInsensitive // @desc Compare a string for equality, case insensitive -// @see Dqn_String8_Eq +// @see Dqn_Str8_Eq -// @proc Dqn_FString8_EqString8Insensitive +// @proc Dqn_FStr8_EqStr8Insensitive // @desc Compare a string for equality, case insensitive -// @see Dqn_String8_Eq +// @see Dqn_Str8_Eq -template struct Dqn_FString8 +template struct Dqn_FStr8 { char data[N+1]; Dqn_usize size; - bool operator==(Dqn_FString8 const &other) const { + bool operator==(Dqn_FStr8 const &other) const { if (size != other.size) return false; bool result = DQN_MEMCMP(data, other.data, size); return result; } - bool operator!=(Dqn_FString8 const &other) const { return !(*this == other); } + bool operator!=(Dqn_FStr8 const &other) const { return !(*this == other); } char *begin() { return data; } char *end () { return data + size; } @@ -438,26 +443,26 @@ template struct Dqn_FString8 char const *end () const { return data + size; } }; -template Dqn_FString8 Dqn_FString8_InitF (DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -template Dqn_usize Dqn_FString8_Max (Dqn_FString8 const *string); -template void Dqn_FString8_Clear (Dqn_FString8 *string); -template bool Dqn_FString8_AppendFV (Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list va); -template bool Dqn_FString8_AppendF (Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -template bool Dqn_FString8_AppendCString8 (Dqn_FString8 *string, char const *value, Dqn_usize size); -template bool Dqn_FString8_Append (Dqn_FString8 *string, Dqn_String8 value); -template Dqn_String8 Dqn_FString8_ToString8 (Dqn_FString8 const *string); -template bool Dqn_FString8_Eq (Dqn_FString8 const *lhs, Dqn_FString8 const *rhs, Dqn_String8EqCase eq_case); -template bool Dqn_FString8_EqString8 (Dqn_FString8 const *lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case); -template bool Dqn_FString8_EqInsensitive (Dqn_FString8 const *lhs, Dqn_FString8 const *rhs); -template bool Dqn_FString8_EqString8Insensitive (Dqn_FString8 const *lhs, Dqn_String8 rhs); -template bool Dqn_FString8_EqFString8 (Dqn_FString8 const *lhs, Dqn_FString8 const *rhs, Dqn_String8EqCase eq_case); -template bool Dqn_FString8_EqFString8Insensitive(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs); -#endif // !defined(DQN_NO_FSTRING8) +template Dqn_FStr8 Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...); +template Dqn_usize Dqn_FStr8_Max (Dqn_FStr8 const *string); +template void Dqn_FStr8_Clear (Dqn_FStr8 *string); +template bool Dqn_FStr8_AppendFV (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list va); +template bool Dqn_FStr8_AppendF (Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...); +template bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8 *string, char const *value, Dqn_usize size); +template bool Dqn_FStr8_Append (Dqn_FStr8 *string, Dqn_Str8 value); +template Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8 const *string); +template bool Dqn_FStr8_Eq (Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs, Dqn_Str8EqCase eq_case); +template bool Dqn_FStr8_EqStr8 (Dqn_FStr8 const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case); +template bool Dqn_FStr8_EqInsensitive (Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs); +template bool Dqn_FStr8_EqStr8Insensitive (Dqn_FStr8 const *lhs, Dqn_Str8 rhs); +template bool Dqn_FStr8_EqFStr8 (Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs, Dqn_Str8EqCase eq_case); +template bool Dqn_FStr8_EqFStr8Insensitive(Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs); +#endif // !defined(DQN_NO_FSTR8) -// NOTE: [$STRB] Dqn_String8Builder ================================================================ +// NOTE: [$STRB] Dqn_Str8Builder ================================================================ // NOTE: API ======================================================================================= -// @proc Dqn_String8Builder_AppendRef, Dqn_String8_AppendCopy, -// Dqn_String8_AppendFV, Dqn_String8_AppendF +// @proc Dqn_Str8Builder_AppendRef, Dqn_Str8_AppendCopy, +// Dqn_Str8_AppendFV, Dqn_Str8_AppendF // @desc Append a string to the list of strings in the builder. // // The string is appended to the builder as follows @@ -472,7 +477,7 @@ template bool Dqn_FString8_EqFString8Insen // @return True if append was successful, false if parameters are invalid // or memory allocation failure. -// @proc Dqn_String8Builder_Build +// @proc Dqn_Str8Builder_Build // @desc Build the list of strings into the final composite string from the // string builder // @param builder The string builder to build the string from @@ -480,20 +485,20 @@ template bool Dqn_FString8_EqFString8Insen // @return The string if build was successful, empty string if parameters are // invalid or memory allocation failure. -struct Dqn_String8Builder +struct Dqn_Str8Builder { - Dqn_Allocator allocator; ///< Allocator to use to back the string list - Dqn_String8Link *head; ///< First string in the linked list of strings - Dqn_String8Link *tail; ///< Last string in the linked list of strings - Dqn_usize string_size; ///< The size in bytes necessary to construct the current string - Dqn_usize count; ///< The number of links in the linked list of strings + Dqn_Allocator allocator; ///< Allocator to use to back the string list + Dqn_Str8Link *head; ///< First string in the linked list of strings + Dqn_Str8Link *tail; ///< Last string in the linked list of strings + Dqn_usize string_size; ///< The size in bytes necessary to construct the current string + Dqn_usize count; ///< The number of links in the linked list of strings }; -DQN_API bool Dqn_String8Builder_AppendF (Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, ...); -DQN_API bool Dqn_String8Builder_AppendFV (Dqn_String8Builder *builder, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args); -DQN_API bool Dqn_String8Builder_AppendRef (Dqn_String8Builder *builder, Dqn_String8 string); -DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_String8 string); -DQN_API Dqn_String8 Dqn_String8Builder_Build (Dqn_String8Builder const *builder, Dqn_Allocator allocator); +DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...); +DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args); +DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string); +DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string); +DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Allocator allocator); // NOTE: [$CHAR] Dqn_Char ========================================================================== DQN_API bool Dqn_Char_IsAlphabet (char ch); @@ -510,38 +515,38 @@ DQN_API char Dqn_Char_ToLower (char ch); DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint); DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint); -#if !defined(DQN_NO_FSTRING8) -// NOTE: [$FSTR] Dqn_FString8 ====================================================================== -template Dqn_FString8 Dqn_FString8_InitF(DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +#if !defined(DQN_NO_FSTR8) +// NOTE: [$FSTR] Dqn_FStr8 ====================================================================== +template Dqn_FStr8 Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...) { - Dqn_FString8 result = {}; + Dqn_FStr8 result = {}; if (fmt) { va_list args; va_start(args, fmt); - Dqn_FString8_AppendFV(&result, fmt, args); + Dqn_FStr8_AppendFV(&result, fmt, args); va_end(args); } return result; } -template Dqn_usize Dqn_FString8_Max(Dqn_FString8 const *) +template Dqn_usize Dqn_FStr8_Max(Dqn_FStr8 const *) { Dqn_usize result = N; return result; } -template void Dqn_FString8_Clear(Dqn_FString8 *string) +template void Dqn_FStr8_Clear(Dqn_FStr8 *string) { *string = {}; } -template bool Dqn_FString8_AppendFV(Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +template bool Dqn_FStr8_AppendFV(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, va_list args) { bool result = false; if (!string || !fmt) return result; - Dqn_usize require = Dqn_CString8_FVSize(fmt, args) + 1 /*null_terminate*/; + Dqn_usize require = Dqn_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/; Dqn_usize space = (N + 1) - string->size; result = require <= space; string->size += STB_SPRINTF_DECORATE(vsnprintf)(string->data + string->size, DQN_CAST(int)space, fmt, args); @@ -552,19 +557,19 @@ template bool Dqn_FString8_AppendFV(Dqn_FString8 *string, DQN_F return result; } -template bool Dqn_FString8_AppendF(Dqn_FString8 *string, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +template bool Dqn_FStr8_AppendF(Dqn_FStr8 *string, DQN_FMT_ATTRIB char const *fmt, ...) { bool result = false; if (!string || !fmt) return result; va_list args; va_start(args, fmt); - result = Dqn_FString8_AppendFV(string, fmt, args); + result = Dqn_FStr8_AppendFV(string, fmt, args); va_end(args); return result; } -template bool Dqn_FString8_AppendCString8(Dqn_FString8 *string, char const *src, Dqn_usize size) +template bool Dqn_FStr8_AppendCStr8(Dqn_FStr8 *string, char const *src, Dqn_usize size) { DQN_ASSERT(string->size <= N); bool result = false; @@ -579,15 +584,15 @@ template bool Dqn_FString8_AppendCString8(Dqn_FString8 *string, return result; } -template bool Dqn_FString8_Append(Dqn_FString8 *string, Dqn_String8 src) +template bool Dqn_FStr8_Append(Dqn_FStr8 *string, Dqn_Str8 src) { - bool result = Dqn_FString8_AppendCString8(string, src.data, src.size); + bool result = Dqn_FStr8_AppendCStr8(string, src.data, src.size); return result; } -template Dqn_String8 Dqn_FString8_ToString8(Dqn_FString8 const *string) +template Dqn_Str8 Dqn_FStr8_ToStr8(Dqn_FStr8 const *string) { - Dqn_String8 result = {}; + Dqn_Str8 result = {}; if (!string || string->size <= 0) return result; @@ -596,49 +601,49 @@ template Dqn_String8 Dqn_FString8_ToString8(Dqn_FString8 const return result; } -template bool Dqn_FString8_Eq(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs, Dqn_String8EqCase eq_case) +template bool Dqn_FStr8_Eq(Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs, Dqn_Str8EqCase eq_case) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - Dqn_String8 rhs_s8 = Dqn_FString8_ToString8(rhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs_s8, eq_case); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case); return result; } -template bool Dqn_FString8_EqString8(Dqn_FString8 const *lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case) +template bool Dqn_FStr8_EqStr8(Dqn_FStr8 const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs, eq_case); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs, eq_case); return result; } -template bool Dqn_FString8_EqInsensitive(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs) +template bool Dqn_FStr8_EqInsensitive(Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - Dqn_String8 rhs_s8 = Dqn_FString8_ToString8(rhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs_s8, Dqn_String8EqCase_Insensitive); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive); return result; } -template bool Dqn_FString8_EqString8Insensitive(Dqn_FString8 const *lhs, Dqn_String8 rhs) +template bool Dqn_FStr8_EqStr8Insensitive(Dqn_FStr8 const *lhs, Dqn_Str8 rhs) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs, Dqn_String8EqCase_Insensitive); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs, Dqn_Str8EqCase_Insensitive); return result; } -template bool Dqn_FString8_EqFString8(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs, Dqn_String8EqCase eq_case) +template bool Dqn_FStr8_EqFStr8(Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs, Dqn_Str8EqCase eq_case) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - Dqn_String8 rhs_s8 = Dqn_FString8_ToString8(rhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs_s8, eq_case); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case); return result; } -template bool Dqn_FString8_EqFString8Insensitive(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs) +template bool Dqn_FStr8_EqFStr8Insensitive(Dqn_FStr8 const *lhs, Dqn_FStr8 const *rhs) { - Dqn_String8 lhs_s8 = Dqn_FString8_ToString8(lhs); - Dqn_String8 rhs_s8 = Dqn_FString8_ToString8(rhs); - bool result = Dqn_String8_Eq(lhs_s8, rhs_s8, Dqn_String8EqCase_Insensitive); + Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs); + Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs); + bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive); return result; } -#endif // !defined(DQN_NO_FSTRING8) +#endif // !defined(DQN_NO_FSTR8) diff --git a/dqn_win32.h b/dqn_win32.h index fefb7cd..1df46f2 100644 --- a/dqn_win32.h +++ b/dqn_win32.h @@ -330,6 +330,9 @@ } OVERLAPPED, *LPOVERLAPPED; // NOTE: um/winbase.h ========================================================================== + #define WAIT_FAILED ((DWORD)0xFFFFFFFF) + #define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 ) + #define INFINITE 0xFFFFFFFF // Wait/Synchronisation: Infinite timeout #define STD_INPUT_HANDLE ((DWORD)-10) @@ -345,6 +348,9 @@ #define FORMAT_MESSAGE_FROM_HMODULE 0x00000800 #define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 + // NOTE: CreateProcessW + #define STARTF_USESTDHANDLES 0x00000100 + extern "C" { __declspec(dllimport) BOOL __stdcall MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags); @@ -708,6 +714,34 @@ } // NOTE: um/processthreadsapi.h ================================================================ + typedef struct _PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; + } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; + + typedef struct _STARTUPINFOW { + DWORD cb; + WCHAR *lpReserved; + WCHAR *lpDesktop; + WCHAR *lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + BYTE *lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + } STARTUPINFOW, *LPSTARTUPINFOW; + typedef DWORD (__stdcall *PTHREAD_START_ROUTINE)( VOID *lpThreadParameter ); @@ -715,8 +749,12 @@ extern "C" { + __declspec(dllimport) BOOL __stdcall CreateProcessW(WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation); __declspec(dllimport) HANDLE __stdcall CreateThread(SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId); __declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID); + __declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode); + __declspec(dllimport) void __stdcall ExitProcess(UINT uExitCode); + } // NOTE: um/memoryapi.h ========================================================================