diff --git a/build.bat b/build.bat index 9e77f5c..7569fba 100644 --- a/build.bat +++ b/build.bat @@ -13,7 +13,7 @@ pushd Build REM O2 Optimisation Level 2 REM Oi Use CPU Intrinsics REM Z7 Combine multi-debug files to one debug file - set common_flags=-D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h + set common_flags=-D DQN_WITH_UNIT_TESTS -D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF %script_dir%\dqn.cpp set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo diff --git a/build.sh b/build.sh index c354a4e..a4505c7 100755 --- a/build.sh +++ b/build.sh @@ -11,10 +11,9 @@ pushd Build -Werror \ -fsanitize=address \ -std=c++17 \ - -D DQN_IMPLEMENTATION \ -D DQN_UNIT_TESTS_WITH_MAIN \ -D DQN_UNIT_TESTS_WITH_KECCAK \ - -x c++ ${code_dir}/dqn.h \ + -x ${code_dir}/dqn.cpp \ -g \ -o dqn_unit_tests popd diff --git a/dqn.cpp b/dqn.cpp new file mode 100644 index 0000000..2e9c258 --- /dev/null +++ b/dqn.cpp @@ -0,0 +1,62 @@ +#include "dqn.h" + +/* +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\ +// \_$$ _|$$$\ $$$ |$$ __$$\ $$ | +// $$ | $$$$\ $$$$ |$$ | $$ |$$ | +// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ | +// $$ | $$ \$$$ $$ |$$ ____/ $$ | +// $$ | $$ |\$ /$$ |$$ | $$ | +// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\ +// \______|\__| \__|\__| \________| +// +// Implementation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + +#if defined(DQN_WITH_CGEN) + #if !defined(DQN_NO_METADESK) + DQN_MSVC_WARNING_PUSH + DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '': unreferenced function with internal linkage has been removed + #include "External/metadesk/md.c" + DQN_MSVC_WARNING_POP + #endif + #define DQN_CPP_FILE_IMPLEMENTATION + #include "Standalone/dqn_cpp_file.h" + #include "dqn_cgen.cpp" +#endif + +#if defined(DQN_WITH_JSON) + #include "dqn_json.cpp" +#endif + +#include "dqn_base.cpp" +#include "dqn_thread_context.cpp" +#include "dqn_external.cpp" +#include "dqn_allocator.cpp" +#include "dqn_debug.cpp" +#include "dqn_string.cpp" +#include "dqn_containers.cpp" +#include "dqn_type_info.cpp" + +#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64) + #include "dqn_os_posix.cpp" +#elif defined(DQN_PLATFORM_WIN32) + #include "dqn_os_win32.cpp" +#else + #error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs +#endif + +#include "dqn_os.cpp" +#include "dqn_math.cpp" +#include "dqn_hash.cpp" +#include "dqn_helpers.cpp" + +#if defined(DQN_WITH_UNIT_TESTS) + #include "dqn_unit_tests.cpp" +#endif + +#include "dqn_docs.cpp" diff --git a/dqn.h b/dqn.h index e954df6..7a52806 100644 --- a/dqn.h +++ b/dqn.h @@ -1,4 +1,4 @@ -#if !defined(DQN_H) +#pragma once #define DQN_H /* @@ -15,6 +15,7 @@ // \___| // // dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dqn +// ASCII -- BigMoney-NW by Nathan Bloomfild // //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -42,11 +43,7 @@ // // -- Compiling -- // -// Define DQN_IMPLEMENTATION macro in one and only one translation unit to -// enable the implementation of this library, for example: -// -// #define DQN_IMEPLEMENTATION -// #include "dqn.h" +// Compile dqn.cpp or include it into one of your translation units. // // Additionally, this library supports including/excluding specific sections // of the library by using #define on the name of the section. These names are @@ -208,6 +205,14 @@ // library from being included. This might be useful if you are including the // library in your project yourself. The library must still be defined and // visible before this header. +// +// - Enable compilation of unit tests with the library. +// +// #define DQN_WITH_UNIT_TESTS +// +// - Increase the capacity of the job queue, default is 128. +// +// #define DQN_JOB_QUEUE_SPMC_SIZE 128 */ #if defined(DQN_ONLY_VARRAY) || \ @@ -343,65 +348,3 @@ #endif #include "dqn_json.h" #endif -#endif // DQN_H - -#if defined(DQN_IMPLEMENTATION) -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\ -// \_$$ _|$$$\ $$$ |$$ __$$\ $$ | -// $$ | $$$$\ $$$$ |$$ | $$ |$$ | -// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ | -// $$ | $$ \$$$ $$ |$$ ____/ $$ | -// $$ | $$ |\$ /$$ |$$ | $$ | -// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\ -// \______|\__| \__|\__| \________| -// -// Implementation -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -#if defined(DQN_WITH_CGEN) - #if !defined(DQN_NO_METADESK) - DQN_MSVC_WARNING_PUSH - DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '': unreferenced function with internal linkage has been removed - #include "External/metadesk/md.c" - DQN_MSVC_WARNING_POP - #endif - #define DQN_CPP_FILE_IMPLEMENTATION - #include "Standalone/dqn_cpp_file.h" - #include "dqn_cgen.cpp" -#endif - -#if defined(DQN_WITH_JSON) - #define DQN_JSON_IMPLEMENTATION - #include "dqn_json.h" -#endif - -#include "dqn_base.cpp" -#include "dqn_thread_context.cpp" -#include "dqn_external.cpp" -#include "dqn_allocator.cpp" -#include "dqn_debug.cpp" -#include "dqn_string.cpp" -#include "dqn_containers.cpp" -#include "dqn_type_info.cpp" - -#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64) - #include "dqn_os_posix.cpp" -#elif defined(DQN_PLATFORM_WIN32) - #include "dqn_os_win32.cpp" -#else - #error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs -#endif - -#include "dqn_os.cpp" -#include "dqn_math.cpp" -#include "dqn_hash.cpp" -#include "dqn_helpers.cpp" - -#include "dqn_unit_tests.cpp" -#include "dqn_docs.cpp" -#endif // DQN_IMPLEMENTATION diff --git a/dqn_allocator.cpp b/dqn_allocator.cpp index d9caed5..74295d3 100644 --- a/dqn_allocator.cpp +++ b/dqn_allocator.cpp @@ -345,7 +345,8 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size) // NOTE: Store the offset to the original pointer behind the user's // pointer. - DQN_MEMCPY(&(DQN_CAST(char *)slot->data)[-1], &offset_to_original_ptr, 1); + char *offset_to_original_storage = DQN_CAST(char *)slot->data - 1; + DQN_MEMCPY(offset_to_original_storage, &offset_to_original_ptr, 1); } // NOTE: Smuggle the slot type in the next pointer so that we know, when the @@ -427,6 +428,7 @@ DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *poo DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, Dqn_Str8 label) { + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_ArenaCatalogItem *result = &catalog->sentinel; for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) { if (item->label == label) { @@ -434,6 +436,7 @@ DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, D break; } } + Dqn_TicketMutex_End(&catalog->ticket_mutex); return result; } @@ -462,7 +465,9 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena * DQN_API void Dqn_ArenaCatalog_AddLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label) { + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label); + Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy); } @@ -470,14 +475,18 @@ DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, { va_list args; va_start(args, fmt); + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); + Dqn_TicketMutex_End(&catalog->ticket_mutex); va_end(args); Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); } DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args) { + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); + Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); } @@ -494,14 +503,18 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelRef(Dqn_ArenaCatalog *catalog, Dqn DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label) { + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label); + Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy); return result; } DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args) { + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); + Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label); return result; } @@ -510,7 +523,9 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize { va_list args; va_start(args, fmt); + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); + Dqn_TicketMutex_End(&catalog->ticket_mutex); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label); va_end(args); return result; diff --git a/dqn_allocator.h b/dqn_allocator.h index 2cecff6..0fdeed1 100644 --- a/dqn_allocator.h +++ b/dqn_allocator.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -51,6 +54,7 @@ struct Dqn_Arena { Dqn_ArenaBlock *curr; uint8_t flags; + Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use. }; struct Dqn_ArenaTempMem diff --git a/dqn_avx512f.cpp b/dqn_avx512f.cpp new file mode 100644 index 0000000..18d0451 --- /dev/null +++ b/dqn_avx512f.cpp @@ -0,0 +1,286 @@ +#pragma once +#include "dqn.h" +#include + +/* +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// /$$$$$$ /$$ /$$ /$$ /$$ /$$$$$$$ /$$ /$$$$$$ /$$$$$$$$ +// /$$__ $$| $$ | $$| $$ / $$ | $$____/ /$$$$ /$$__ $$| $$_____/ +// | $$ \ $$| $$ | $$| $$/ $$/ | $$ |_ $$ |__/ \ $$| $$ +// | $$$$$$$$| $$ / $$/ \ $$$$/ /$$$$$$| $$$$$$$ | $$ /$$$$$$/| $$$$$ +// | $$__ $$ \ $$ $$/ >$$ $$|______/|_____ $$ | $$ /$$____/ | $$__/ +// | $$ | $$ \ $$$/ /$$/\ $$ /$$ \ $$ | $$ | $$ | $$ +// | $$ | $$ \ $/ | $$ \ $$ | $$$$$$//$$$$$$| $$$$$$$$| $$ +// |__/ |__/ \_/ |__/ |__/ \______/|______/|________/|__/ +// +// dqn_avx512f.h +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + +DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8AVX512F(Dqn_Str8 string, Dqn_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + Dqn_Str8FindResult result = {}; + if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + Dqn_usize const search_size = string.size - find.size; + Dqn_usize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data; + + while (simd_iterations--) { + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DQN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DQN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DQN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DQN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = Dqn_Str8_Init(string.data, result.index); + result.match = Dqn_Str8_Init(string.data + result.index, find.size); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - result.index); + return result; + } + + zero_byte_mask = Dqn_Bit_ClearNextLSB(zero_byte_mask); + } + + ptr += sizeof(__m512i); + } + + for (Dqn_usize index = ptr - string.data; index < string.size; index++) { + Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find.size); + if (Dqn_Str8_Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = Dqn_Str8_Init(string.data, index); + result.match = Dqn_Str8_Init(string.data + index, find.size); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index); + return result; + } + } + + return result; +} + +DQN_API Dqn_Str8FindResult Dqn_Str8_FindLastStr8AVX512F(Dqn_Str8 string, Dqn_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + Dqn_Str8FindResult result = {}; + if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + Dqn_usize const search_size = string.size - find.size; + Dqn_usize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data + search_size + 1; + + while (simd_iterations--) { + ptr -= sizeof(__m512i); + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DQN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DQN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DQN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DQN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = Dqn_Str8_Init(string.data, result.index); + result.match = Dqn_Str8_Init(string.data + result.index, find.size); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - result.index); + return result; + } + + zero_byte_mask = Dqn_Bit_ClearNextLSB(zero_byte_mask); + } + } + + for (Dqn_usize index = ptr - string.data - 1; index < string.size; index--) { + Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find.size); + if (Dqn_Str8_Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = Dqn_Str8_Init(string.data, index); + result.match = Dqn_Str8_Init(string.data + index, find.size); + result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index); + return result; + } + } + + return result; +} + +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitAVX512F(Dqn_Str8 string, Dqn_Str8 find) +{ + Dqn_Str8BinarySplitResult result = {}; + Dqn_Str8FindResult find_result = Dqn_Str8_FindStr8AVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = Dqn_Str8_Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastAVX512F(Dqn_Str8 string, Dqn_Str8 find) +{ + Dqn_Str8BinarySplitResult result = {}; + Dqn_Str8FindResult find_result = Dqn_Str8_FindLastStr8AVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = Dqn_Str8_Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DQN_API Dqn_usize Dqn_Str8_SplitAVX512F(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode) +{ + Dqn_usize result = 0; // The number of splits in the actual string. + if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(delimiter) || delimiter.size <= 0) + return result; + + Dqn_Str8BinarySplitResult split = {}; + Dqn_Str8 first = string; + do { + split = Dqn_Str8_BinarySplitAVX512F(first, delimiter); + if (split.lhs.size || mode == Dqn_Str8SplitIncludeEmptyStrings_Yes) { + if (splits && result < splits_count) + splits[result] = split.lhs; + result++; + } + first = split.rhs; + } while (first.size); + + return result; +} + +DQN_API Dqn_Slice Dqn_Str8_SplitAllocAVX512F(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode) +{ + Dqn_Slice result = {}; + Dqn_usize splits_required = Dqn_Str8_SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode); + result.data = Dqn_Arena_NewArray(arena, Dqn_Str8, splits_required, Dqn_ZeroMem_No); + if (result.data) { + result.size = Dqn_Str8_SplitAVX512F(string, delimiter, result.data, splits_required, mode); + DQN_ASSERT(splits_required == result.size); + } + return result; +} diff --git a/dqn_avx512f.h b/dqn_avx512f.h new file mode 100644 index 0000000..771f23c --- /dev/null +++ b/dqn_avx512f.h @@ -0,0 +1,30 @@ +#if !defined(DQN_AVX512F_H) +#define DQN_AVX512F_H + +/* +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// $$$$$$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$$\ +// $$ __$$\ $$ | $$ |$$ | $$ | $$ ____| $$$$ | $$ __$$\ $$ _____| +// $$ / $$ |$$ | $$ |\$$\ $$ | $$ | \_$$ | \__/ $$ |$$ | +// $$$$$$$$ |\$$\ $$ | \$$$$ /$$$$$$\ $$$$$$$\ $$ | $$$$$$ |$$$$$\ +// $$ __$$ | \$$\$$ / $$ $$< \______|\_____$$\ $$ | $$ ____/ $$ __| +// $$ | $$ | \$$$ / $$ /\$$\ $$\ $$ | $$ | $$ | $$ | +// $$ | $$ | \$ / $$ / $$ | \$$$$$$ |$$$$$$\ $$$$$$$$\ $$ | +// \__| \__| \_/ \__| \__| \______/ \______|\________|\__| +// +// dqn_avx512f.h -- Functions implemented w/ AVX512 +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + +#include "dqn.h" + +DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8AVX512F (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8FindResult Dqn_Str8_FindLastStr8AVX512F (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitAVX512F (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastAVX512F(Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_usize Dqn_Str8_SplitAVX512F (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode); +DQN_API Dqn_Slice Dqn_Str8_SplitAllocAVX512F (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode); + +#endif // DQN_AVX512F_H diff --git a/dqn_base.cpp b/dqn_base.cpp index 2e54591..9a2a5a0 100644 --- a/dqn_base.cpp +++ b/dqn_base.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -21,16 +24,196 @@ #include #endif -DQN_API Dqn_CPUIDRegisters Dqn_CPUID(int function_id) +Dqn_CPUFeatureDecl g_dqn_cpu_feature_decl[Dqn_CPUFeature_Count]; + +DQN_API Dqn_CPUIDResult Dqn_CPU_ID(Dqn_CPUIDArgs args) { - Dqn_CPUIDRegisters result = {}; - #if defined(DQN_COMPILER_MSVC) - __cpuid(DQN_CAST(int *)result.array, function_id); - #elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) - __get_cpuid(function_id, &result.array[0] /*eax*/, &result.array[1] /*ebx*/, &result.array[2] /*ecx*/ , &result.array[3] /*edx*/); - #else - #error "Compiler not supported" - #endif + Dqn_CPUIDResult result = {}; + __cpuidex(result.values, args.eax, args.ecx); + return result; +} + +DQN_API Dqn_usize Dqn_CPU_HasFeatureArray(Dqn_CPUReport const *report, Dqn_CPUFeatureQuery *features, Dqn_usize features_size) +{ + Dqn_usize result = 0; + Dqn_usize const BITS = sizeof(report->features[0]) * 8; + DQN_FOR_UINDEX(feature_index, features_size) { + Dqn_CPUFeatureQuery *query = features + feature_index; + Dqn_usize chunk_index = query->feature / BITS; + Dqn_usize chunk_bit = query->feature % BITS; + uint64_t chunk = report->features[chunk_index]; + query->available = chunk & (1ULL << chunk_bit); + result += DQN_CAST(int)query->available; + } + + return result; +} + +DQN_API bool Dqn_CPU_HasFeature(Dqn_CPUReport const *report, Dqn_CPUFeature feature) +{ + Dqn_CPUFeatureQuery query = {}; + query.feature = feature; + bool result = Dqn_CPU_HasFeatureArray(report, &query, 1) == 1; + return result; +} + +DQN_API bool Dqn_CPU_HasAllFeatures(Dqn_CPUReport const *report, Dqn_CPUFeature const *features, Dqn_usize features_size) +{ + bool result = true; + for (Dqn_usize index = 0; result && index < features_size; index++) + result &= Dqn_CPU_HasFeature(report, features[index]); + return result; +} + +DQN_API void Dqn_CPU_SetFeature(Dqn_CPUReport *report, Dqn_CPUFeature feature) +{ + DQN_ASSERT(feature < Dqn_CPUFeature_Count); + Dqn_usize const BITS = sizeof(report->features[0]) * 8; + Dqn_usize chunk_index = feature / BITS; + Dqn_usize chunk_bit = feature % BITS; + report->features[chunk_index] |= (1ULL << chunk_bit); +} + +DQN_API Dqn_CPUReport Dqn_CPU_Report() +{ + Dqn_CPUReport result = {}; + Dqn_CPUIDResult fn_0000_[16] = {}; + Dqn_CPUIDResult fn_8000_[64] = {}; + int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000; + int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax); + + // NOTE: Query standard/extended numbers /////////////////////////////////////////////////////// + { + Dqn_CPUIDArgs args = {}; + + // NOTE: Query standard function (e.g. eax = 0x0) for function count + cpu vendor + args = {}; + fn_0000_[0] = Dqn_CPU_ID(args); + + // NOTE: Query extended function (e.g. eax = 0x8000'0000) for function count + cpu vendor + args = {}; + args.eax = DQN_CAST(int) EXTENDED_FUNC_BASE_EAX; + fn_8000_[0] = Dqn_CPU_ID(args); + } + + // NOTE: Extract function count //////////////////////////////////////////////////////////////// + int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax; + int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax; + + // NOTE: Enumerate all CPUID results for the known function counts ///////////////////////////// + { + DQN_ASSERT((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_)); + DQN_ASSERT((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_)); + + for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) { + Dqn_CPUIDArgs args = {}; + args.eax = eax; + fn_0000_[eax] = Dqn_CPU_ID(args); + } + + for (int eax = EXTENDED_FUNC_BASE_EAX + 1, index = 1; eax <= EXTENDED_FUNC_MAX_EAX; eax++, index++) { + Dqn_CPUIDArgs args = {}; + args.eax = eax; + fn_8000_[index] = Dqn_CPU_ID(args); + } + } + + // NOTE: Query CPU vendor ////////////////////////////////////////////////////////////////////// + { + DQN_MEMCPY(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE); + DQN_MEMCPY(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE); + DQN_MEMCPY(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE); + } + + // NOTE: Query CPU brand /////////////////////////////////////////////////////////////////////// + if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) { + DQN_MEMCPY(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 8, &fn_8000_[0x0002].reg.ecx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 12, &fn_8000_[0x0002].reg.edx, REGISTER_SIZE); + + DQN_MEMCPY(result.brand + 16, &fn_8000_[0x0003].reg.eax, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 20, &fn_8000_[0x0003].reg.ebx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 24, &fn_8000_[0x0003].reg.ecx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 28, &fn_8000_[0x0003].reg.edx, REGISTER_SIZE); + + DQN_MEMCPY(result.brand + 32, &fn_8000_[0x0004].reg.eax, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 36, &fn_8000_[0x0004].reg.ebx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 40, &fn_8000_[0x0004].reg.ecx, REGISTER_SIZE); + DQN_MEMCPY(result.brand + 44, &fn_8000_[0x0004].reg.edx, REGISTER_SIZE); + + DQN_ASSERT(result.brand[sizeof(result.brand) - 1] == 0); + } + + // NOTE: Query CPU features ////////////////////////////////////////////////////////////////// + for (Dqn_usize ext_index = 0; ext_index < Dqn_CPUFeature_Count; ext_index++) { + bool available = false; + + // NOTE: Mask bits taken from various manuals + // - AMD64 Architecture Programmer's Manual, Volumes 1-5 + // - https://en.wikipedia.org/wiki/CPUID#Calling_CPUID + switch (DQN_CAST(Dqn_CPUFeature)ext_index) { + case Dqn_CPUFeature_3DNow: available = (fn_8000_[0x0001].reg.edx & (1 << 31)); break; + case Dqn_CPUFeature_3DNowExt: available = (fn_8000_[0x0001].reg.edx & (1 << 30)); break; + case Dqn_CPUFeature_ABM: available = (fn_8000_[0x0001].reg.ecx & (1 << 5)); break; + case Dqn_CPUFeature_AES: available = (fn_0000_[0x0001].reg.ecx & (1 << 25)); break; + case Dqn_CPUFeature_AVX: available = (fn_0000_[0x0001].reg.ecx & (1 << 28)); break; + case Dqn_CPUFeature_AVX2: available = (fn_0000_[0x0007].reg.ebx & (1 << 0)); break; + case Dqn_CPUFeature_AVX512F: available = (fn_0000_[0x0007].reg.ebx & (1 << 16)); break; + case Dqn_CPUFeature_AVX512DQ: available = (fn_0000_[0x0007].reg.ebx & (1 << 17)); break; + case Dqn_CPUFeature_AVX512IFMA: available = (fn_0000_[0x0007].reg.ebx & (1 << 21)); break; + case Dqn_CPUFeature_AVX512PF: available = (fn_0000_[0x0007].reg.ebx & (1 << 26)); break; + case Dqn_CPUFeature_AVX512ER: available = (fn_0000_[0x0007].reg.ebx & (1 << 27)); break; + case Dqn_CPUFeature_AVX512CD: available = (fn_0000_[0x0007].reg.ebx & (1 << 28)); break; + case Dqn_CPUFeature_AVX512BW: available = (fn_0000_[0x0007].reg.ebx & (1 << 30)); break; + case Dqn_CPUFeature_AVX512VL: available = (fn_0000_[0x0007].reg.ebx & (1 << 31)); break; + case Dqn_CPUFeature_AVX512VBMI: available = (fn_0000_[0x0007].reg.ecx & (1 << 1)); break; + case Dqn_CPUFeature_AVX512VBMI2: available = (fn_0000_[0x0007].reg.ecx & (1 << 6)); break; + case Dqn_CPUFeature_AVX512VNNI: available = (fn_0000_[0x0007].reg.ecx & (1 << 11)); break; + case Dqn_CPUFeature_AVX512BITALG: available = (fn_0000_[0x0007].reg.ecx & (1 << 12)); break; + case Dqn_CPUFeature_AVX512VPOPCNTDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 14)); break; + case Dqn_CPUFeature_AVX5124VNNIW: available = (fn_0000_[0x0007].reg.edx & (1 << 2)); break; + case Dqn_CPUFeature_AVX5124FMAPS: available = (fn_0000_[0x0007].reg.edx & (1 << 3)); break; + case Dqn_CPUFeature_AVX512VP2INTERSECT: available = (fn_0000_[0x0007].reg.edx & (1 << 8)); break; + case Dqn_CPUFeature_AVX512FP16: available = (fn_0000_[0x0007].reg.edx & (1 << 23)); break; + case Dqn_CPUFeature_CLZERO: available = (fn_8000_[0x0008].reg.ebx & (1 << 0)); break; + case Dqn_CPUFeature_CMPXCHG8B: available = (fn_0000_[0x0001].reg.edx & (1 << 8)); break; + case Dqn_CPUFeature_CMPXCHG16B: available = (fn_0000_[0x0001].reg.ecx & (1 << 13)); break; + case Dqn_CPUFeature_F16C: available = (fn_0000_[0x0001].reg.ecx & (1 << 29)); break; + case Dqn_CPUFeature_FMA: available = (fn_0000_[0x0001].reg.ecx & (1 << 12)); break; + case Dqn_CPUFeature_FMA4: available = (fn_8000_[0x0001].reg.ecx & (1 << 16)); break; + case Dqn_CPUFeature_FP128: available = (fn_8000_[0x001A].reg.eax & (1 << 0)); break; + case Dqn_CPUFeature_FP256: available = (fn_8000_[0x001A].reg.eax & (1 << 2)); break; + case Dqn_CPUFeature_FPU: available = (fn_0000_[0x0001].reg.edx & (1 << 0)); break; + case Dqn_CPUFeature_MMX: available = (fn_0000_[0x0001].reg.edx & (1 << 23)); break; + case Dqn_CPUFeature_MONITOR: available = (fn_0000_[0x0001].reg.ecx & (1 << 3)); break; + case Dqn_CPUFeature_MOVBE: available = (fn_0000_[0x0001].reg.ecx & (1 << 22)); break; + case Dqn_CPUFeature_MOVU: available = (fn_8000_[0x001A].reg.eax & (1 << 1)); break; + case Dqn_CPUFeature_MmxExt: available = (fn_8000_[0x0001].reg.edx & (1 << 22)); break; + case Dqn_CPUFeature_PCLMULQDQ: available = (fn_0000_[0x0001].reg.ecx & (1 << 1)); break; + case Dqn_CPUFeature_POPCNT: available = (fn_0000_[0x0001].reg.ecx & (1 << 23)); break; + case Dqn_CPUFeature_RDRAND: available = (fn_0000_[0x0001].reg.ecx & (1 << 30)); break; + case Dqn_CPUFeature_RDSEED: available = (fn_0000_[0x0007].reg.ebx & (1 << 18)); break; + case Dqn_CPUFeature_RDTSCP: available = (fn_8000_[0x0001].reg.edx & (1 << 27)); break; + case Dqn_CPUFeature_SHA: available = (fn_0000_[0x0007].reg.ebx & (1 << 29)); break; + case Dqn_CPUFeature_SSE: available = (fn_0000_[0x0001].reg.edx & (1 << 25)); break; + case Dqn_CPUFeature_SSE2: available = (fn_0000_[0x0001].reg.edx & (1 << 26)); break; + case Dqn_CPUFeature_SSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 0)); break; + case Dqn_CPUFeature_SSE41: available = (fn_0000_[0x0001].reg.ecx & (1 << 19)); break; + case Dqn_CPUFeature_SSE42: available = (fn_0000_[0x0001].reg.ecx & (1 << 20)); break; + case Dqn_CPUFeature_SSE4A: available = (fn_8000_[0x0001].reg.ecx & (1 << 6)); break; + case Dqn_CPUFeature_SSSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 9)); break; + case Dqn_CPUFeature_TSC: available = (fn_0000_[0x0001].reg.edx & (1 << 4)); break; + case Dqn_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break; + case Dqn_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break; + case Dqn_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break; + case Dqn_CPUFeature_Count: DQN_INVALID_CODE_PATH; break; + } + + if (available) + Dqn_CPU_SetFeature(&result, DQN_CAST(Dqn_CPUFeature)ext_index); + } + return result; } #endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN) diff --git a/dqn_base.h b/dqn_base.h index 6aa3b53..d3d0751 100644 --- a/dqn_base.h +++ b/dqn_base.h @@ -73,6 +73,7 @@ #include #include #include +#include // PRIu64... #if !defined(DQN_OS_WIN32) #include // exit() @@ -455,10 +456,110 @@ struct Dqn_ErrorSink #endif #if !defined(DQN_PLATFORM_ARM64) -struct Dqn_CPUIDRegisters +struct Dqn_CPURegisters { - Dqn_uint array[4]; // Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX) + int eax; + int ebx; + int ecx; + int edx; }; + +union Dqn_CPUIDResult +{ + Dqn_CPURegisters reg; + int values[4]; +}; + +struct Dqn_CPUIDArgs +{ + int eax; + int ecx; +}; + +#define DQN_CPU_FEAT_XMACRO \ + DQN_CPU_FEAT_XENTRY(3DNow) \ + DQN_CPU_FEAT_XENTRY(3DNowExt) \ + DQN_CPU_FEAT_XENTRY(ABM) \ + DQN_CPU_FEAT_XENTRY(AES) \ + DQN_CPU_FEAT_XENTRY(AVX) \ + DQN_CPU_FEAT_XENTRY(AVX2) \ + DQN_CPU_FEAT_XENTRY(AVX512F) \ + DQN_CPU_FEAT_XENTRY(AVX512DQ) \ + DQN_CPU_FEAT_XENTRY(AVX512IFMA) \ + DQN_CPU_FEAT_XENTRY(AVX512PF) \ + DQN_CPU_FEAT_XENTRY(AVX512ER) \ + DQN_CPU_FEAT_XENTRY(AVX512CD) \ + DQN_CPU_FEAT_XENTRY(AVX512BW) \ + DQN_CPU_FEAT_XENTRY(AVX512VL) \ + DQN_CPU_FEAT_XENTRY(AVX512VBMI) \ + DQN_CPU_FEAT_XENTRY(AVX512VBMI2) \ + DQN_CPU_FEAT_XENTRY(AVX512VNNI) \ + DQN_CPU_FEAT_XENTRY(AVX512BITALG) \ + DQN_CPU_FEAT_XENTRY(AVX512VPOPCNTDQ) \ + DQN_CPU_FEAT_XENTRY(AVX5124VNNIW) \ + DQN_CPU_FEAT_XENTRY(AVX5124FMAPS) \ + DQN_CPU_FEAT_XENTRY(AVX512VP2INTERSECT) \ + DQN_CPU_FEAT_XENTRY(AVX512FP16) \ + DQN_CPU_FEAT_XENTRY(CLZERO) \ + DQN_CPU_FEAT_XENTRY(CMPXCHG8B) \ + DQN_CPU_FEAT_XENTRY(CMPXCHG16B) \ + DQN_CPU_FEAT_XENTRY(F16C) \ + DQN_CPU_FEAT_XENTRY(FMA) \ + DQN_CPU_FEAT_XENTRY(FMA4) \ + DQN_CPU_FEAT_XENTRY(FP128) \ + DQN_CPU_FEAT_XENTRY(FP256) \ + DQN_CPU_FEAT_XENTRY(FPU) \ + DQN_CPU_FEAT_XENTRY(MMX) \ + DQN_CPU_FEAT_XENTRY(MONITOR) \ + DQN_CPU_FEAT_XENTRY(MOVBE) \ + DQN_CPU_FEAT_XENTRY(MOVU) \ + DQN_CPU_FEAT_XENTRY(MmxExt) \ + DQN_CPU_FEAT_XENTRY(PCLMULQDQ) \ + DQN_CPU_FEAT_XENTRY(POPCNT) \ + DQN_CPU_FEAT_XENTRY(RDRAND) \ + DQN_CPU_FEAT_XENTRY(RDSEED) \ + DQN_CPU_FEAT_XENTRY(RDTSCP) \ + DQN_CPU_FEAT_XENTRY(SHA) \ + DQN_CPU_FEAT_XENTRY(SSE) \ + DQN_CPU_FEAT_XENTRY(SSE2) \ + DQN_CPU_FEAT_XENTRY(SSE3) \ + DQN_CPU_FEAT_XENTRY(SSE41) \ + DQN_CPU_FEAT_XENTRY(SSE42) \ + DQN_CPU_FEAT_XENTRY(SSE4A) \ + DQN_CPU_FEAT_XENTRY(SSSE3) \ + DQN_CPU_FEAT_XENTRY(TSC) \ + DQN_CPU_FEAT_XENTRY(TscInvariant) \ + DQN_CPU_FEAT_XENTRY(VAES) \ + DQN_CPU_FEAT_XENTRY(VPCMULQDQ) + +enum Dqn_CPUFeature +{ + #define DQN_CPU_FEAT_XENTRY(label) Dqn_CPUFeature_##label, + DQN_CPU_FEAT_XMACRO + #undef DQN_CPU_FEAT_XENTRY + Dqn_CPUFeature_Count, +}; + +struct Dqn_CPUFeatureDecl +{ + Dqn_CPUFeature value; + Dqn_Str8 label; +}; + +struct Dqn_CPUFeatureQuery +{ + Dqn_CPUFeature feature; + bool available; +}; + +struct Dqn_CPUReport +{ + char vendor [4 /*bytes*/ * 3 /*EDX, ECX, EBX*/ + 1 /*null*/]; + char brand [48]; + uint64_t features[(Dqn_CPUFeature_Count / (sizeof(uint64_t) * 8)) + 1]; +}; + +extern Dqn_CPUFeatureDecl g_dqn_cpu_feature_decl[Dqn_CPUFeature_Count]; #endif // DQN_PLATFORM_ARM64 // NOTE: [$TMUT] Dqn_TicketMutex /////////////////////////////////////////////////////////////////// @@ -468,6 +569,17 @@ struct Dqn_TicketMutex unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned }; +// NOTE: [$MUTX] Dqn_OSMutex /////////////////////////////////////////////////////////////////////// +struct Dqn_OSMutex +{ + #if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS) + char win32_handle[48]; + #else + pthread_mutex_t posix_handle; + pthread_mutexattr_t posix_attribs; + #endif +}; + // NOTE: [$PRIN] Dqn_Print ///////////////////////////////////////////////////////////////////////// enum Dqn_PrintStd { @@ -516,7 +628,13 @@ typedef void Dqn_LogProc(Dqn_Str8 type, DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value); DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value); #if !defined(DQN_PLATFORM_ARM64) -DQN_API Dqn_CPUIDRegisters Dqn_CPUID (int function_id); +DQN_API Dqn_CPUIDResult Dqn_CPU_ID (Dqn_CPUIDArgs args); +DQN_API Dqn_usize Dqn_CPU_HasFeatureArray (Dqn_CPUReport const *report, Dqn_CPUFeatureQuery *features, Dqn_usize features_size); +DQN_API bool Dqn_CPU_HasFeature (Dqn_CPUReport const *report, Dqn_CPUFeature feature); +DQN_API bool Dqn_CPU_HasAllFeatures (Dqn_CPUReport const *report, Dqn_CPUFeature const *features, Dqn_usize features_size); +template bool Dqn_CPU_HasAllFeaturesCArray (Dqn_CPUReport const *report, Dqn_CPUFeature const (&features)[N]); +DQN_API void Dqn_CPU_SetFeature (Dqn_CPUReport *report, Dqn_CPUFeature feature); +DQN_API Dqn_CPUReport Dqn_CPU_Report (); #endif // NOTE: [$TMUT] Dqn_TicketMutex /////////////////////////////////////////////////////////////////// @@ -671,3 +789,9 @@ DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value) #error Unsupported compiler #endif } + +template bool Dqn_CPU_HasAllFeaturesCArray(Dqn_CPUReport const *report, Dqn_CPUFeature const (&features)[N]) +{ + bool result = Dqn_CPU_HasAllFeatures(report, features, N); + return result; +} diff --git a/dqn_cgen.cpp b/dqn_cgen.cpp index 8a8df0c..e663ded 100644 --- a/dqn_cgen.cpp +++ b/dqn_cgen.cpp @@ -45,6 +45,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] = Dqn_CGenTableHeaderType_CppType, Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppIsPtr, + Dqn_CGenTableHeaderType_CppOpEquals, Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_GenTypeInfo, @@ -320,6 +321,7 @@ DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8(Dqn_CGenTableHeaderType type case Dqn_CGenTableHeaderType_CppName: result = DQN_STR8("cpp_name"); break; case Dqn_CGenTableHeaderType_CppValue: result = DQN_STR8("cpp_value"); break; case Dqn_CGenTableHeaderType_CppIsPtr: result = DQN_STR8("cpp_is_ptr"); break; + case Dqn_CGenTableHeaderType_CppOpEquals: result = DQN_STR8("cpp_op_equals"); break; case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break; case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break; case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break; @@ -626,6 +628,46 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C } break; } } + + // NOTE: Str8 to enum conversion //////////////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + if (table->type != Dqn_CGenTableType_CodeGenEnum) + continue; + + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) { + Dqn_CppLine(cpp, "bool success;"); + Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name)); + } + } + } + + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + if (table->type != Dqn_CGenTableType_CodeGenEnum) + continue; + + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppLine(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); + } + } + + // NOTE: Operator == and != //////////////////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + if (table->type != Dqn_CGenTableType_CodeGenStruct) + continue; + + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + Dqn_Str8 cpp_op_equals = it.cgen_table_column[Dqn_CGenTableHeaderType_CppOpEquals].string; + if (cpp_op_equals != DQN_STR8("true")) + continue; + + Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppLine(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); + Dqn_CppLine(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)); + } + } } if (emit & Dqn_CGenEmit_Implementation) { @@ -685,7 +727,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size); Dqn_CppLine(cpp, - "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},", + "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*salignof(%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},", row_index, DQN_STR_FMT(cpp_name.column.string), cpp_name_padding, "", @@ -700,6 +742,10 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C DQN_STR_FMT(cpp_name.column.string), cpp_name_padding, "", + // NOTE: alignof(a->b) + DQN_STR_FMT(cpp_type.column.string), + cpp_type_padding, "", + // NOTE: Type string DQN_STR_FMT(cpp_type.column.string), cpp_type_padding, "", @@ -726,7 +772,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index); Dqn_CppLine(cpp, - "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},", + "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), alignof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},", row_index, // NOTE: Name string @@ -739,6 +785,9 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C // NOTE: sizeof(a) DQN_STR_FMT(struct_or_enum_name), + // NOTE: alignof(a) + DQN_STR_FMT(struct_or_enum_name), + // NOTE: ..._Type_... DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name)); @@ -812,17 +861,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C } } + // NOTE: Str8 to enum conversion //////////////////////////////////////////////////////////// for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { if (table->type != Dqn_CGenTableType_CodeGenEnum) continue; for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; - Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) { - Dqn_CppLine(cpp, "bool success;"); - Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name)); - } - Dqn_CppFuncBlock(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) { Dqn_CppLine(cpp, "%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(type_name)); Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s_type_fields); index++", DQN_STR_FMT(type_name)) { @@ -838,5 +883,84 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C } } } + + // NOTE: Operator == and != //////////////////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + if (table->type != Dqn_CGenTableType_CodeGenStruct) + continue; + + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + Dqn_Str8 cpp_op_equals = it.cgen_table_column[Dqn_CGenTableHeaderType_CppOpEquals].string; + if (cpp_op_equals != DQN_STR8("true")) + continue; + + Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppFuncBlock(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) { + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row); + Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row); + Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row); + + // TODO(doyle): Check if we're an integral type or not to double check if we + // can use memcmp or operator== + if (Dqn_Str8_HasData(cpp_array_size_field.column.string)) { + Dqn_CppIfChain(cpp) { + Dqn_CppIfOrElseIfBlock(cpp, + "lhs.%.*s != rhs.%.*s", + DQN_STR_FMT(cpp_array_size_field.column.string), + DQN_STR_FMT(cpp_array_size_field.column.string)) { + Dqn_CppLine(cpp, "return false;"); + } + } + Dqn_CppIfChain(cpp) { + Dqn_CppIfOrElseIfBlock(cpp, + "DQN_MEMCMP(lhs.%.*s, rhs.%.*s, lhs.%.*s) != 0", + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_array_size_field.column.string)) { + Dqn_CppLine(cpp, "return false;"); + } + } + } else if (Dqn_Str8_HasData(cpp_array_size.column.string)) { + Dqn_CppIfChain(cpp) { + Dqn_CppIfOrElseIfBlock(cpp, + "DQN_MEMCMP(lhs.%.*s, rhs.%.*s, %.*s) != 0", + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_array_size.column.string)) { + Dqn_CppLine(cpp, "return false;"); + } + } + } else if (cpp_is_ptr.column.string == DQN_STR8("true")) { + Dqn_CppIfChain(cpp) { + Dqn_CppIfOrElseIfBlock(cpp, + "*lhs.%.*s != *rhs.%.*s", + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_name.column.string)) { + Dqn_CppLine(cpp, "return false;"); + } + } + } else { + Dqn_CppIfChain(cpp) { + Dqn_CppIfOrElseIfBlock(cpp, + "lhs.%.*s != rhs.%.*s", + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_name.column.string)) { + Dqn_CppLine(cpp, "return false;"); + } + } + } + } + Dqn_CppLine(cpp, "return true;"); + } + + Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) { + Dqn_CppLine(cpp, "bool result = !(lhs == rhs);"); + Dqn_CppLine(cpp, "return result;"); + } + } + } } } diff --git a/dqn_cgen.h b/dqn_cgen.h index 9471619..ecb7a0d 100644 --- a/dqn_cgen.h +++ b/dqn_cgen.h @@ -67,6 +67,7 @@ enum Dqn_CGenTableHeaderType Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppValue, Dqn_CGenTableHeaderType_CppIsPtr, + Dqn_CGenTableHeaderType_CppOpEquals, Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_GenTypeInfo, diff --git a/dqn_containers.cpp b/dqn_containers.cpp index 7c6e00a..85623da 100644 --- a/dqn_containers.cpp +++ b/dqn_containers.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_containers.h b/dqn_containers.h index 41f15b1..65ef3d1 100644 --- a/dqn_containers.h +++ b/dqn_containers.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -281,6 +284,8 @@ template void Dqn_FArra #define DQN_TO_SLICE(val) Dqn_Slice_Init((val)->data, (val)->size) template Dqn_Slice Dqn_Slice_Init (T* const data, Dqn_usize size); template Dqn_Slice Dqn_Slice_InitCArray (Dqn_Arena *arena, T const (&array)[N]); +template Dqn_Slice Dqn_Slice_Copy (Dqn_Arena *arena, Dqn_Slice slice); +template Dqn_Slice Dqn_Slice_CopyPtr (Dqn_Arena *arena, T* const data, Dqn_usize size); template Dqn_Slice Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice array, Dqn_Str8 separator); Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice array); @@ -622,7 +627,7 @@ template bool Dqn_VArray_Reserve(Dqn_VArray *array, Dqn_usize co #endif // !defined(DQN_NO_VARRAY) #if !defined(DQN_NO_SARRAY) -// NOTE: [$FARR] Dqn_SArray //////////////////////////////////////////////////////////////////////// +// NOTE: [$SARR] Dqn_SArray //////////////////////////////////////////////////////////////////////// template Dqn_SArray Dqn_SArray_Init(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) { Dqn_SArray result = {}; @@ -700,7 +705,7 @@ template T *Dqn_SArray_Add(Dqn_SArray *array, T const &item) return result; } -template T *Dqn_SArray_InsertArray(Dqn_SArray *array, Dqn_usize index, T const *items, Dqn_usize count) +template T *Dqn_SArray_InsertArray(Dqn_SArray *array, Dqn_usize index, T const *items, Dqn_usize count) { T *result = nullptr; if (!Dqn_SArray_IsValid(array)) @@ -715,7 +720,7 @@ template T *Dqn_SArray_InsertCArray(Dqn_SArray *arr return result; } -template T *Dqn_SArray_Insert(Dqn_SArray *array, Dqn_usize index, T const &item) +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; @@ -896,6 +901,19 @@ Dqn_Slice Dqn_Slice_InitCArray(Dqn_Arena *arena, T const (&array)[N]) return result; } +template Dqn_Slice Dqn_Slice_CopyPtr(Dqn_Arena *arena, T *const data, Dqn_usize size) +{ + T *copy = Dqn_Arena_NewArrayCopy(arena, T, data, size); + Dqn_Slice result = Dqn_Slice_Init(copy, copy ? size : 0); + return result; +} + +template Dqn_Slice Dqn_Slice_Copy(Dqn_Arena *arena, Dqn_Slice slice) +{ + Dqn_Slice result = Dqn_Slice_CopyPtr(arena, slice.data, slice.size); + return result; +} + template Dqn_Slice Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) { Dqn_Slice result = {}; @@ -1442,7 +1460,7 @@ template DQN_API T *Dqn_List_At(Dqn_List *list, Dqn_usize index, if (!list || !list->chunk_size || index >= list->count) return nullptr; - Dqn_usize total_chunks = list->count / (list->chunk_size + (list->chunk_size - 1)); + Dqn_usize total_chunks = (list->count / list->chunk_size) + ((list->chunk_size % list->count) ? 1 : 0); Dqn_usize desired_chunk = index / list->chunk_size; Dqn_usize forward_scan_dist = desired_chunk; Dqn_usize backward_scan_dist = total_chunks - desired_chunk; @@ -1452,11 +1470,11 @@ template DQN_API T *Dqn_List_At(Dqn_List *list, Dqn_usize index, Dqn_usize current_chunk = 0; Dqn_ListChunk **chunk = nullptr; if (forward_scan_dist <= backward_scan_dist) { - for (chunk = &list->head; *chunk && current_chunk != desired_chunk; *chunk = (*chunk)->next, current_chunk++) { + for (chunk = &list->head; *chunk && current_chunk != desired_chunk; chunk = &((*chunk)->next), current_chunk++) { } } else { current_chunk = total_chunks; - for (chunk = &list->tail; *chunk && current_chunk != desired_chunk; *chunk = (*chunk)->prev, current_chunk--) { + for (chunk = &list->tail; *chunk && current_chunk != desired_chunk; chunk = &((*chunk)->prev), current_chunk--) { } } diff --git a/dqn_debug.cpp b/dqn_debug.cpp index 237bb33..32ccd99 100644 --- a/dqn_debug.cpp +++ b/dqn_debug.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_debug.h b/dqn_debug.h index 9b55592..ff6665e 100644 --- a/dqn_debug.h +++ b/dqn_debug.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_external.h b/dqn_external.h index e4dca21..6ba7861 100644 --- a/dqn_external.h +++ b/dqn_external.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_hash.cpp b/dqn_hash.cpp index fc10cf1..6232a75 100644 --- a/dqn_hash.cpp +++ b/dqn_hash.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_hash.h b/dqn_hash.h index 7abc289..d17806d 100644 --- a/dqn_hash.h +++ b/dqn_hash.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_helpers.cpp b/dqn_helpers.cpp index cd25774..cd37abc 100644 --- a/dqn_helpers.cpp +++ b/dqn_helpers.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -249,222 +252,6 @@ DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, b } #endif // !defined(DQN_NO_JSON_BUILDER) -#if !defined(DQN_NO_BIN) -// NOTE: [$BHEX] Dqn_Bin /////////////////////////////////////////////////////////////////////////// -DQN_API char const *Dqn_Bin_HexBufferTrim0x(char const *hex, Dqn_usize size, Dqn_usize *real_size) -{ - 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_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_Str8 result = Dqn_Str8_Init(trimmed, trimmed_size); - return result; -} - -DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8(uint64_t number, uint32_t flags) -{ - Dqn_Str8 prefix = {}; - if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) - prefix = DQN_STR8("0x"); - - Dqn_BinHexU64Str8 result = {}; - DQN_MEMCPY(result.data, prefix.data, prefix.size); - result.size += DQN_CAST(int8_t) prefix.size; - - char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; - int size = DQN_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)); - - // NOTE: snprintf returns the required size of the format string - // irrespective of if there's space or not, but, always null terminates so - // the last byte is wasted. - result.size = DQN_MIN(result.size, DQN_ARRAY_UCOUNT(result.data) - 1); - return result; -} - -DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags) -{ - Dqn_Str8 prefix = {}; - if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) - prefix = DQN_STR8("0x"); - - 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_Alloc(arena, required_size, Dqn_ZeroMem_No); - - if (Dqn_Str8_HasData(result)) { - DQN_MEMCPY(result.data, prefix.data, prefix.size); - int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/ - DQN_SNPRINTF(result.data + prefix.size, space, fmt, number); - } - return result; -} - -DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_Str8 hex) -{ - Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X")); - Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/; - DQN_ASSERT(real_hex.size <= max_hex_size); - - Dqn_usize size = DQN_MIN(max_hex_size, real_hex.size); - uint64_t result = 0; - for (Dqn_usize index = 0; index < size; index++) { - char ch = real_hex.data[index]; - if (!Dqn_Char_IsHex(ch)) - break; - uint8_t val = Dqn_Char_HexToU8(ch); - result = (result << 4) | val; - } - return result; -} - -DQN_API uint64_t Dqn_Bin_HexPtrToU64(char const *hex, Dqn_usize size) -{ - uint64_t result = Dqn_Bin_HexToU64(Dqn_Str8_Init(hex, size)); - return result; -} - -DQN_API bool Dqn_Bin_BytesToHexBuffer(void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size) -{ - if (!src || !dest) - return false; - - if (!DQN_CHECK(dest_size >= src_size * 2)) - return false; - - char const *HEX = "0123456789abcdef"; - unsigned char const *src_u8 = DQN_CAST(unsigned char const *) src; - for (Dqn_usize src_index = 0, dest_index = 0; src_index < src_size; src_index++) { - char byte = src_u8[src_index]; - char hex01 = (byte >> 4) & 0b1111; - char hex02 = (byte >> 0) & 0b1111; - dest[dest_index++] = HEX[(int)hex01]; - dest[dest_index++] = HEX[(int)hex02]; - } - - return true; -} - -DQN_API char *Dqn_Bin_BytesToHexBufferArena(Dqn_Arena *arena, void const *src, Dqn_usize size) -{ - char *result = - size > 0 ? Dqn_Arena_NewArray(arena, char, (size * 2) + 1 /*null terminate*/, Dqn_ZeroMem_No) : nullptr; - if (result) { - bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2); - DQN_ASSERT(converted); - result[size * 2] = 0; - } - return result; -} - -DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena(Dqn_Arena *arena, void const *src, Dqn_usize size) -{ - Dqn_Str8 result = {}; - result.data = Dqn_Bin_BytesToHexBufferArena(arena, src, size); - if (result.data) - result.size = size * 2; - return result; -} - -DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size) -{ - Dqn_usize result = 0; - if (!hex || hex_size <= 0) - return result; - - Dqn_usize trim_size = 0; - char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, hex_size, &trim_size); - - // NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB" - // Either way, the size can be odd or even, hence we round up to the nearest - // multiple of two to ensure that we calculate the min buffer size orrectly. - Dqn_usize trim_size_rounded_up = trim_size + (trim_size % 2); - Dqn_usize min_buffer_size = trim_size_rounded_up / 2; - if (dest_size < min_buffer_size || trim_size <= 0) { - DQN_ASSERTF(dest_size >= min_buffer_size, "Insufficient buffer size for converting hex to binary"); - return result; - } - - result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, dest, dest_size); - return result; -} - -DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, - Dqn_usize hex_size, - void *dest, - Dqn_usize dest_size) -{ - Dqn_usize result = 0; - unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest; - - for (Dqn_usize hex_index = 0; hex_index < hex_size; hex_index += 2, result += 1) { - char hex01 = hex[hex_index]; - char hex02 = (hex_index + 1 < hex_size) ? hex[hex_index + 1] : 0; - - char bit4_01 = (hex01 >= '0' && hex01 <= '9') ? 0 + (hex01 - '0') - : (hex01 >= 'a' && hex01 <= 'f') ? 10 + (hex01 - 'a') - : (hex01 >= 'A' && hex01 <= 'F') ? 10 + (hex01 - 'A') - : 0; - - char bit4_02 = (hex02 >= '0' && hex02 <= '9') ? 0 + (hex02 - '0') - : (hex02 >= 'a' && hex02 <= 'f') ? 10 + (hex02 - 'a') - : (hex02 >= 'A' && hex02 <= 'F') ? 10 + (hex02 - 'A') - : 0; - - char byte = (bit4_01 << 4) | (bit4_02 << 0); - dest_u8[result] = byte; - } - - DQN_ASSERT(result <= dest_size); - return result; -} - -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_Str8 hex, void *dest, Dqn_usize dest_size) -{ - Dqn_usize result = Dqn_Bin_HexBufferToBytes(hex.data, hex.size, dest, dest_size); - return result; -} - -DQN_API char *Dqn_Bin_HexBufferToBytesArena(Dqn_Arena *arena, char const *hex, Dqn_usize size, Dqn_usize *real_size) -{ - char *result = nullptr; - 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); - if (result) { - Dqn_usize convert_size = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, result, binary_size); - if (real_size) - *real_size = convert_size; - } - return result; -} - -DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_Str8 hex) -{ - Dqn_Str8 result = {}; - result.data = Dqn_Bin_HexBufferToBytesArena(arena, hex.data, hex.size, &result.size); - return result; -} -#endif // !defined(DQN_NO_BIN) - // NOTE: [$BITS] Dqn_Bit /////////////////////////////////////////////////////////////////////////// DQN_API void Dqn_Bit_UnsetInplace(Dqn_usize *flags, Dqn_usize bitfield) { @@ -1033,6 +820,162 @@ DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_usize type) return result; } +DQN_API uint64_t Dqn_HexToU64(Dqn_Str8 hex) +{ + Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X")); + Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/; + DQN_ASSERT(real_hex.size <= max_hex_size); + + Dqn_usize size = DQN_MIN(max_hex_size, real_hex.size); + uint64_t result = 0; + for (Dqn_usize index = 0; index < size; index++) { + char ch = real_hex.data[index]; + Dqn_CharHexToU8 val = Dqn_Char_HexToU8(ch); + if (!val.success) + break; + result = (result << 4) | val.value; + } + return result; +} + +DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags) +{ + Dqn_Str8 prefix = {}; + if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + prefix = DQN_STR8("0x"); + + 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_Alloc(arena, required_size, Dqn_ZeroMem_No); + + if (Dqn_Str8_HasData(result)) { + DQN_MEMCPY(result.data, prefix.data, prefix.size); + int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/ + DQN_SNPRINTF(result.data + prefix.size, space, fmt, number); + } + return result; +} + +DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8(uint64_t number, uint32_t flags) +{ + Dqn_Str8 prefix = {}; + if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) + prefix = DQN_STR8("0x"); + + Dqn_U64HexStr8 result = {}; + DQN_MEMCPY(result.data, prefix.data, prefix.size); + result.size += DQN_CAST(int8_t) prefix.size; + + char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; + int size = DQN_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)); + + // NOTE: snprintf returns the required size of the format string + // irrespective of if there's space or not, but, always null terminates so + // the last byte is wasted. + result.size = DQN_MIN(result.size, DQN_ARRAY_UCOUNT(result.data) - 1); + return result; +} + +DQN_API bool Dqn_BytesToHexPtr(void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size) +{ + if (!src || !dest) + return false; + + if (!DQN_CHECK(dest_size >= src_size * 2)) + return false; + + char const *HEX = "0123456789abcdef"; + unsigned char const *src_u8 = DQN_CAST(unsigned char const *) src; + for (Dqn_usize src_index = 0, dest_index = 0; src_index < src_size; src_index++) { + char byte = src_u8[src_index]; + char hex01 = (byte >> 4) & 0b1111; + char hex02 = (byte >> 0) & 0b1111; + dest[dest_index++] = HEX[(int)hex01]; + dest[dest_index++] = HEX[(int)hex02]; + } + + return true; +} + +DQN_API Dqn_Str8 Dqn_BytesToHex(Dqn_Arena *arena, void const *src, Dqn_usize size) +{ + Dqn_Str8 result = {}; + if (!src || size <= 0) + return result; + + result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No); + result.data[result.size - 1] = 0; + bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size); + DQN_ASSERT(converted); + return result; +} + +DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked(Dqn_Str8 hex, void *dest, Dqn_usize dest_size) +{ + Dqn_usize result = 0; + unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest; + + for (Dqn_usize hex_index = 0; hex_index < hex.size; hex_index += 2, result += 1) { + char hex01 = hex.data[hex_index]; + char hex02 = (hex_index + 1 < hex.size) ? hex.data[hex_index + 1] : 0; + char bit4_01 = Dqn_Char_HexToU8(hex01).value; + char bit4_02 = Dqn_Char_HexToU8(hex02).value; + char byte = (bit4_01 << 4) | (bit4_02 << 0); + dest_u8[result] = byte; + } + + DQN_ASSERT(result <= dest_size); + return result; +} + +DQN_API Dqn_usize Dqn_HexToBytesPtr(Dqn_Str8 hex, void *dest, Dqn_usize dest_size) +{ + hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")); + hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0X")); + + Dqn_usize result = 0; + if (!Dqn_Str8_HasData(hex)) + return result; + + // NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB" + // Either way, the size can be odd or even, hence we round up to the nearest + // multiple of two to ensure that we calculate the min buffer size orrectly. + Dqn_usize hex_size_rounded_up = hex.size + (hex.size % 2); + Dqn_usize min_buffer_size = hex_size_rounded_up / 2; + if (hex.size <= 0 || !DQN_CHECK(dest_size >= min_buffer_size)) { + return result; + } + + result = Dqn_HexToBytesPtrUnchecked(hex, dest, dest_size); + return result; +} + +DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked(Dqn_Arena *arena, Dqn_Str8 hex) +{ + Dqn_Str8 result = Dqn_Str8_Alloc(arena, hex.size / 2, Dqn_ZeroMem_No); + if (result.data) { + Dqn_usize bytes_written = Dqn_HexToBytesPtr(hex, result.data, result.size); + DQN_ASSERT(bytes_written == result.size); + } + return result; +} + +DQN_API Dqn_Str8 Dqn_HexToBytes(Dqn_Arena *arena, Dqn_Str8 hex) +{ + hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")); + hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0X")); + + Dqn_Str8 result = {}; + if (!DQN_CHECK(Dqn_Str8_IsAll(hex, Dqn_Str8IsAll_Hex))) + return result; + + result = Dqn_HexToBytesUnchecked(arena, hex); + return result; +} + + // NOTE: [$DLIB] Dqn_Library /////////////////////////////////////////////////////////////////////// Dqn_Library *g_dqn_library; @@ -1052,7 +995,13 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) if (result->lib_init) return result; - result->lib_init = true; + + #define DQN_CPU_FEAT_XENTRY(label) g_dqn_cpu_feature_decl[Dqn_CPUFeature_##label] = {Dqn_CPUFeature_##label, DQN_STR8(#label)}; + DQN_CPU_FEAT_XMACRO + #undef DQN_CPU_FEAT_XENTRY + + result->lib_init = true; + result->cpu_report = Dqn_CPU_Report(); // NOTE: Query OS info ///////////////////////////////////////////////////////////////////////// { @@ -1070,7 +1019,6 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) #endif } - // NOTE Initialise fields ////////////////////////////////////////////////////////////////////// #if !defined(DQN_NO_PROFILER) result->profiler = &result->profiler_default_instance; @@ -1104,10 +1052,9 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) result->exe_dir = Dqn_OS_EXEDir(&result->arena); // NOTE: Print out init features /////////////////////////////////////////////////////////////// - if (on_init == Dqn_LibraryOnInit_LogFeatures) { - Dqn_Str8Builder builder = {}; - builder.arena = scratch.arena; - + Dqn_Str8Builder builder = {}; + builder.arena = scratch.arena; + if (on_init & Dqn_LibraryOnInit_LogLibFeatures) { Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n")); Dqn_f64 page_size_kib = result->os_page_size / 1024.0; @@ -1138,10 +1085,37 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) #endif // TODO(doyle): Add stacktrace feature log - - Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena); - Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log)); } + + if (on_init & Dqn_LibraryOnInit_LogCPUFeatures) { + Dqn_CPUReport const *report = &result->cpu_report; + Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1)); + Dqn_Str8Builder_AppendF(&builder, + " CPU '%.*s' from '%s' detected:\n", + DQN_STR_FMT(brand), + report->vendor); + + Dqn_usize longest_feature_name = 0; + DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) { + Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index]; + longest_feature_name = DQN_MAX(longest_feature_name, feature_decl.label.size); + } + + DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) { + Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index]; + bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value); + Dqn_Str8Builder_AppendF(&builder, + " %.*s:%*s%s\n", + DQN_STR_FMT(feature_decl.label), + DQN_CAST(int)(longest_feature_name - feature_decl.label.size), + "", + has_feature ? "available" : "not available"); + } + } + + Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena); + if (Dqn_Str8_HasData(info_log)) + Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log)); return result; } @@ -1350,7 +1324,7 @@ DQN_API bool Dqn_OS_JobQueueSPMCAddArray(Dqn_JobQueueSPMC *queue, Dqn_Job *jobs, return false; for (size_t offset = 0; offset < count; offset++) { - uint32_t wrapped_write_index = (write_index + offset) & pot_mask; + uint32_t wrapped_write_index = (write_index + offset) & pot_mask; queue->jobs[wrapped_write_index] = jobs[offset]; } @@ -1413,7 +1387,7 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread) DQN_API void Dqn_OS_JobQueueSPMCWaitForCompletion(Dqn_JobQueueSPMC *queue) { Dqn_OS_MutexLock(&queue->mutex); - if (queue->read_index == queue->write_index) { + if (queue->finish_index == queue->write_index) { Dqn_OS_MutexUnlock(&queue->mutex); return; } diff --git a/dqn_helpers.h b/dqn_helpers.h index ed732c7..40324cc 100644 --- a/dqn_helpers.h +++ b/dqn_helpers.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -50,21 +53,6 @@ struct Dqn_JSONBuilder }; #endif // !defined(DQN_NO_JSON_BUIDLER) -#if !defined(DQN_NO_BIN) -// NOTE: [$BHEX] Dqn_Bin /////////////////////////////////////////////////////////////////////////// -struct Dqn_BinHexU64Str8 -{ - char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/]; - uint8_t size; -}; - -enum Dqn_BinHexU64Str8Flags -{ - Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string - Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex -}; -#endif // !defined(DQN_NO_BIN) - // NOTE: [$BSEA] Dqn_BinarySearch ////////////////////////////////////////////////////////////////// template using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); @@ -112,7 +100,7 @@ struct Dqn_BinarySearchResult // NOTE: [$MISC] Misc ////////////////////////////////////////////////////////////////////////////// struct Dqn_U64Str8 { - char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor + char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separator uint8_t size; }; @@ -146,6 +134,18 @@ enum Dqn_U64AgeUnit Dqn_U64AgeUnit_All = Dqn_U64AgeUnit_HMS | Dqn_U64AgeUnit_Day | Dqn_U64AgeUnit_Week | Dqn_U64AgeUnit_Year, }; +struct Dqn_U64HexStr8 +{ + char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/]; + uint8_t size; +}; + +enum Dqn_U64HexStr8Flags +{ + Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string + Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex +}; + #if !defined(DQN_NO_PROFILER) // NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// #if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE) @@ -182,10 +182,17 @@ struct Dqn_ProfilerZoneScope ~Dqn_ProfilerZoneScope(); Dqn_ProfilerZone zone; }; -#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) +#define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeWithIndex(name, __COUNTER__ + 1) #endif +#define Dqn_Profiler_ZoneBlockIndex(name, index) \ + for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneWithIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \ + DQN_UNIQUE_NAME(dummy__).begin_tsc == 0; \ + Dqn_Profiler_EndZone(DQN_UNIQUE_NAME(profile_zone__)), DQN_UNIQUE_NAME(dummy__).begin_tsc = 1) + +#define Dqn_Profiler_ZoneBlock(name) Dqn_Profiler_ZoneBlockIndex(DQN_STR8(name), __COUNTER__ + 1) + enum Dqn_ProfilerAnchorBuffer { Dqn_ProfilerAnchorBuffer_Back, @@ -207,10 +214,13 @@ struct Dqn_Job bool add_to_completion_queue; Dqn_Arena *arena; Dqn_JobQueueFunc *func; - uint32_t user_tag; void *user_context; }; +#if !defined(DQN_JOB_QUEUE_SPMC_SIZE) + #define DQN_JOB_QUEUE_SPMC_SIZE 128 +#endif + struct Dqn_JobQueueSPMC { Dqn_OSMutex mutex; @@ -218,7 +228,7 @@ struct Dqn_JobQueueSPMC Dqn_OSSemaphore wait_for_completion_semaphore; uint32_t threads_waiting_for_completion; - Dqn_Job jobs[64]; + Dqn_Job jobs[DQN_JOB_QUEUE_SPMC_SIZE]; Dqn_b32 quit; uint32_t quit_exit_code; uint32_t volatile read_index; @@ -226,7 +236,7 @@ struct Dqn_JobQueueSPMC uint32_t volatile write_index; Dqn_OSSemaphore complete_queue_write_semaphore; - Dqn_Job complete_queue[64]; + Dqn_Job complete_queue[DQN_JOB_QUEUE_SPMC_SIZE]; uint32_t volatile complete_read_index; uint32_t volatile complete_write_index; }; @@ -238,11 +248,13 @@ struct Dqn_Library { bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` Dqn_TicketMutex lib_mutex; + Dqn_TicketMutex thread_context_init_mutex; Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Arena arena; Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations Dqn_ArenaCatalog arena_catalog; bool slow_verification_checks; // Enable expensive library verification checks + Dqn_CPUReport cpu_report; // NOTE: Logging /////////////////////////////////////////////////////////////////////////////// Dqn_LogProc * log_callback; // Set this pointer to override the logging routine void * log_user_data; @@ -275,8 +287,10 @@ struct Dqn_Library enum Dqn_LibraryOnInit { - Dqn_LibraryOnInit_Nil, - Dqn_LibraryOnInit_LogFeatures, + Dqn_LibraryOnInit_Nil = 0, + Dqn_LibraryOnInit_LogLibFeatures = 1 << 0, + Dqn_LibraryOnInit_LogCPUFeatures = 1 << 1, + Dqn_LibraryOnInit_LogAllFeatures = Dqn_LibraryOnInit_LogLibFeatures | Dqn_LibraryOnInit_LogCPUFeatures, }; // NOTE: [$PCGX] Dqn_PCG32 ///////////////////////////////////////////////////////////////////////// @@ -331,32 +345,6 @@ DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBu #define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STR8(""), value) #define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value) #endif // !defined(DQN_NO_JSON_BUILDER) -#if !defined(DQN_NO_BIN) -// NOTE: [$BHEX] Dqn_Bin /////////////////////////////////////////////////////////////////////////// -// TODO(doyle): I'm not happy with this API. Its ugly and feels complicated -// because I designed it as a C-API first (e.g. ptr + length) vs just accepting -// that Dqn_Str8/Dqn_Slice is a superior API to design for first. -DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size); -DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string); - -DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags); -DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags); - -DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex); -DQN_API uint64_t Dqn_Bin_HexPtrToU64 (char const *hex, 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); - -DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size); -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_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 ////////////////////////////////////////////////////////////////// template bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); @@ -371,6 +359,7 @@ DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield); DQN_API bool Dqn_Bit_IsSet (Dqn_usize bits, Dqn_usize bits_to_set); DQN_API bool Dqn_Bit_IsNotSet (Dqn_usize bits, Dqn_usize bits_to_check); +#define Dqn_Bit_ClearNextLSB(value) (value) & ((value) - 1) // NOTE: [$SAFE] Dqn_Safe ////////////////////////////////////////////////////////////////////////// DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b); @@ -439,6 +428,18 @@ DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type); DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type); +DQN_API uint64_t Dqn_HexToU64 (Dqn_Str8 hex); +DQN_API Dqn_Str8 Dqn_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags); +DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8 (uint64_t number, uint32_t flags); + +DQN_API bool Dqn_BytesToHexPtr (Dqn_Arena *arena, void const *src, Dqn_usize src_size, char *dest); +DQN_API Dqn_Str8 Dqn_BytesToHex (Dqn_Arena *arena, void const *src, Dqn_usize size); + +DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek); +DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek); +DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked (Dqn_Arena *arena, Dqn_Str8 hex); +DQN_API Dqn_Str8 Dqn_HexToBytes (Dqn_Arena *arena, Dqn_Str8 hex); + // NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// #define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1) DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index); diff --git a/dqn_json.cpp b/dqn_json.cpp new file mode 100644 index 0000000..154f5dc --- /dev/null +++ b/dqn_json.cpp @@ -0,0 +1,429 @@ +#pragma once +#include "dqn.h" + +// NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// +void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count) +{ + void *result = NULL; + if (!user_data) + return result; + + Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data; + result = Dqn_Arena_Alloc(arena, count, alignof(json_value_s), Dqn_ZeroMem_No); + return result; +} + +char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size) +{ + switch (type) { + case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; } + case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; } + case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; } + case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; } + case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; } + case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; } + + default: /*FALLTHRU*/ + case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; } + } +} + +bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_Str8 key) +{ + bool result = false; + if (lhs && Dqn_Str8_HasData(key)) { + Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size); + result = Dqn_Str8_Eq(lhs_string, key); + } + return result; +} + +// NOTE: Dqn_JSON_It /////////////////////////////////////////////////////////////////////////////// +Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json) +{ + json_parse_result_s parse_result = {}; + json_value_ex_s *ex_value = + DQN_CAST(json_value_ex_s *) json_parse_ex(json.data, + json.size, + json_parse_flags_allow_location_information, + Dqn_JSON_ArenaAllocFunc, + arena, + &parse_result); + + Dqn_JSONIt result = {}; + Dqn_JSON_ItPushValue(&result, &ex_value->value); + return result; +} + +// NOTE: Dqn_JSON_ItPush/Pop /////////////////////////////////////////////////////////////////////// +bool Dqn_JSON_ItPushObjElement(Dqn_JSONIt *it, json_object_element_s *element) +{ + if (!it || !element) + return false; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObjElement, element}; + return true; +} + +bool Dqn_JSON_ItPushObj(Dqn_JSONIt *it, json_object_s *obj) +{ + if (!it || !obj) + return false; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObj, obj}; + return true; +} + +bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element) +{ + if (!it || !element) + return false; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArrayElement, element}; + return true; +} + +bool Dqn_JSON_ItPushArray(Dqn_JSONIt *it, json_value_s *value) +{ + if (!it || !value || json_value_as_array(value) == nullptr) + return false; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArray, value}; + return true; +} + +bool Dqn_JSON_ItPushValue(Dqn_JSONIt *it, json_value_s *value) +{ + bool result = false; + if (!it || !value) + return result; + + if (value->type == json_type_object) { + result = Dqn_JSON_ItPushObj(it, json_value_as_object(value)); + } else if (value->type == json_type_array) { + result = Dqn_JSON_ItPushArray(it, value); + } + + return result; +} + +void Dqn_JSON_ItPop(Dqn_JSONIt *it) +{ + if (!it) + return; + DQN_ASSERT(it->stack_count > 0); + if (it->stack_count > 0) + it->stack_count--; +} + +// NOTE: Dqn_JSON_It JSON tree navigation ////////////////////////////////////////////////////////// +json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it) +{ + json_value_s *result = nullptr; + Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); + if (!curr) + return result; + + if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { + json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value; + result = element->value; + } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { + json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value; + result = element->value; + } else { + result = DQN_CAST(json_value_s *) curr->value; + } + + if (result->type == json_type_array) { + json_array_s *array = json_value_as_array(result); + DQN_ASSERT(array); + Dqn_JSON_ItPushArray(it, result); + } else if (result->type == json_type_object) { + json_object_s *obj = json_value_as_object(result); + DQN_ASSERT(obj); + Dqn_JSON_ItPushObj(it, obj); + } + + return result; +} + +bool Dqn_JSON_ItNext(Dqn_JSONIt *it) +{ + Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); + if (!curr) + return false; + + json_object_element_s *obj_element = nullptr; + json_array_element_s *array_element = nullptr; + if (curr->type == Dqn_JSON_ItEntryTypeObj) { + auto *obj = DQN_CAST(json_object_s *) curr->value; + obj_element = obj->start; + } else if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { + auto *element = DQN_CAST(json_object_element_s *) curr->value; + obj_element = element->next; + Dqn_JSON_ItPop(it); + } else if (curr->type == Dqn_JSON_ItEntryTypeArray) { + auto *value = DQN_CAST(json_value_s *) curr->value; + auto *array = json_value_as_array(value); + array_element = array->start; + } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { + auto *element = DQN_CAST(json_array_element_s *) curr->value; + array_element = element->next; + Dqn_JSON_ItPop(it); + } else { + Dqn_JSON_ItPop(it); + } + + if (obj_element) + Dqn_JSON_ItPushObjElement(it, obj_element); + else if (array_element) + Dqn_JSON_ItPushArrayElement(it, array_element); + + bool result = obj_element || array_element; + return result; +} + +// NOTE: Dqn_JSON_ItCurr /////////////////////////////////////////////////////////////////////////// +Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it) +{ + Dqn_JSONItEntry *result = nullptr; + if (!it || it->stack_count <= 0) + return result; + + result = &it->stack[it->stack_count - 1]; + return result; +} + +json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it) +{ + json_value_s *result = nullptr; + Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); + if (!curr) + return result; + + if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { + auto *element = DQN_CAST(json_object_element_s *)curr->value; + result = element->value; + } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { + auto *element = DQN_CAST(json_array_element_s *)curr->value; + result = element->value; + } else if (curr->type == Dqn_JSON_ItEntryTypeString || + curr->type == Dqn_JSON_ItEntryTypeNumber || + curr->type == Dqn_JSON_ItEntryTypeObj || + curr->type == Dqn_JSON_ItEntryTypeArray) + { + result = DQN_CAST(json_value_s *)curr->value; + } + + return result; +} + +json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it) +{ + Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); + auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement) + ? DQN_CAST(json_object_element_s *) curr->value + : nullptr; + return result; +} + +// NOTE: Dqn_JSON_ItValueIs //////////////////////////////////////////////////////////////////////// +json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_value_s *result = (curr && type == curr->type) ? curr : nullptr; + return result; +} + +json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_object_s *result = curr ? json_value_as_object(curr) : nullptr; + return result; +} + +json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_array_s *result = curr ? json_value_as_array(curr) : nullptr; + return result; +} + +json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_string_s *result = curr ? json_value_as_string(curr) : nullptr; + return result; +} + +json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_number_s *result = curr ? json_value_as_number(curr) : nullptr; + return result; +} + +json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr; + return result; +} + +json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it) +{ + json_value_s *curr = Dqn_JSON_ItCurrValue(it); + json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr; + return result; +} + +size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it) +{ + size_t result = 0; + if (json_array_s *curr = Dqn_JSON_ItValueIsArray(it)) + result = curr->length; + return result; +} + + +// NOTE: Dqn_JSON_ItKeyValueIs ///////////////////////////////////////////////////////////////////// +Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + Dqn_Str8 result = {}; + if (curr) { + result.data = DQN_CAST(char *)curr->name->string; + result.size = curr->name->string_size; + } + return result; +} + +bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + bool result = Dqn_JSON_String8Cmp(curr->name, key); + return result; +} + +json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_s *result = nullptr; + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_object(curr->value); + return result; +} + +json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_array_s *result = nullptr; + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_array(curr->value); + return result; +} + +json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + json_string_s *result = nullptr; + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_string(curr->value); + return result; +} + +json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + json_number_s *result = nullptr; + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_number(curr->value); + return result; +} + +json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + json_value_s *result = nullptr; + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr; + return result; +} + +json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key) +{ + json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); + json_value_s *result = nullptr; + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = curr->value->type == json_type_null ? curr->value : nullptr; + return result; +} + + +// NOTE: Dqn_JSON_ItValueTo //////////////////////////////////////////////////////////////////////// +Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it) +{ + Dqn_Str8 result = {}; + if (json_string_s *curr = Dqn_JSON_ItValueIsString(it)) + result = Dqn_Str8_Init(curr->string, curr->string_size); + return result; +} + +int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it) +{ + int64_t result = {}; + if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it)) + result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value; + return result; +} + +uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it) +{ + uint64_t result = {}; + if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it)) + result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value; + return result; +} + +bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it) +{ + bool result = {}; + if (json_value_s *curr = Dqn_JSON_ItValueIsBool(it)) + result = curr->type == json_type_true; + return result; +} + +void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site) +{ + if (!it) + return; + + json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it); + if (!curr) + return; + + size_t value_type_size = 0; + char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)curr->value->type, &value_type_size); + + json_string_s const *key = curr->name; + if (it->flags & json_parse_flags_allow_location_information) { + json_string_ex_s const *info = DQN_CAST(json_string_ex_s const *)key; + Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, + call_site, + "Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]", + info->line_no, + info->row_no, + DQN_CAST(int)key->string_size, + key->string, + DQN_CAST(int)value_type_size, + value_type); + } else { + Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, + call_site, + "Unknown key-value pair in object [key=%.*s, value=%.*s]", + DQN_CAST(int)key->string_size, + key->string, + DQN_CAST(int)value_type_size, + value_type); + } +} diff --git a/dqn_json.h b/dqn_json.h index 92bb856..be2823c 100644 --- a/dqn_json.h +++ b/dqn_json.h @@ -1,9 +1,10 @@ +#pragma once +#include "dqn.h" + #if !defined(SHEREDOM_JSON_H_INCLUDED) #error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file #endif -#if !defined(DQN_JSON_H) -#define DQN_JSON_H // NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count); @@ -86,434 +87,3 @@ bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it); #define Dqn_JSON_ItErrorUnknownKeyValue(it) Dqn_JSON_ItErrorUnknownKeyValue_(it, DQN_CALL_SITE) void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site); - -#endif // DQN_JSON_H - -#if defined(DQN_JSON_IMPLEMENTATION) -// NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// -void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count) -{ - void *result = NULL; - if (!user_data) - return result; - - Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data; - result = Dqn_Arena_Alloc(arena, count, alignof(json_value_s), Dqn_ZeroMem_No); - return result; -} - -char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size) -{ - switch (type) { - case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; } - case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; } - case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; } - case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; } - case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; } - case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; } - - default: /*FALLTHRU*/ - case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; } - } -} - -bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_Str8 key) -{ - bool result = false; - if (lhs && Dqn_Str8_HasData(key)) { - Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size); - result = Dqn_Str8_Eq(lhs_string, key); - } - return result; -} - -// NOTE: Dqn_JSON_It /////////////////////////////////////////////////////////////////////////////// -Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json) -{ - json_parse_result_s parse_result = {}; - json_value_ex_s *ex_value = - DQN_CAST(json_value_ex_s *) json_parse_ex(json.data, - json.size, - json_parse_flags_allow_location_information, - Dqn_JSON_ArenaAllocFunc, - arena, - &parse_result); - - Dqn_JSONIt result = {}; - Dqn_JSON_ItPushValue(&result, &ex_value->value); - return result; -} - -// NOTE: Dqn_JSON_ItPush/Pop /////////////////////////////////////////////////////////////////////// -bool Dqn_JSON_ItPushObjElement(Dqn_JSONIt *it, json_object_element_s *element) -{ - if (!it || !element) - return false; - DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); - it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObjElement, element}; - return true; -} - -bool Dqn_JSON_ItPushObj(Dqn_JSONIt *it, json_object_s *obj) -{ - if (!it || !obj) - return false; - DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); - it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObj, obj}; - return true; -} - -bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element) -{ - if (!it || !element) - return false; - DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); - it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArrayElement, element}; - return true; -} - -bool Dqn_JSON_ItPushArray(Dqn_JSONIt *it, json_value_s *value) -{ - if (!it || !value || json_value_as_array(value) == nullptr) - return false; - DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); - it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArray, value}; - return true; -} - -bool Dqn_JSON_ItPushValue(Dqn_JSONIt *it, json_value_s *value) -{ - bool result = false; - if (!it || !value) - return result; - - if (value->type == json_type_object) { - result = Dqn_JSON_ItPushObj(it, json_value_as_object(value)); - } else if (value->type == json_type_array) { - result = Dqn_JSON_ItPushArray(it, value); - } - - return result; -} - -void Dqn_JSON_ItPop(Dqn_JSONIt *it) -{ - if (!it) - return; - DQN_ASSERT(it->stack_count > 0); - if (it->stack_count > 0) - it->stack_count--; -} - -// NOTE: Dqn_JSON_It JSON tree navigation ////////////////////////////////////////////////////////// -json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it) -{ - json_value_s *result = nullptr; - Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); - if (!curr) - return result; - - if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { - json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value; - result = element->value; - } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { - json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value; - result = element->value; - } else { - result = DQN_CAST(json_value_s *) curr->value; - } - - if (result->type == json_type_array) { - json_array_s *array = json_value_as_array(result); - DQN_ASSERT(array); - Dqn_JSON_ItPushArray(it, result); - } else if (result->type == json_type_object) { - json_object_s *obj = json_value_as_object(result); - DQN_ASSERT(obj); - Dqn_JSON_ItPushObj(it, obj); - } - - return result; -} - -bool Dqn_JSON_ItNext(Dqn_JSONIt *it) -{ - Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); - if (!curr) - return false; - - json_object_element_s *obj_element = nullptr; - json_array_element_s *array_element = nullptr; - if (curr->type == Dqn_JSON_ItEntryTypeObj) { - auto *obj = DQN_CAST(json_object_s *) curr->value; - obj_element = obj->start; - } else if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { - auto *element = DQN_CAST(json_object_element_s *) curr->value; - obj_element = element->next; - Dqn_JSON_ItPop(it); - } else if (curr->type == Dqn_JSON_ItEntryTypeArray) { - auto *value = DQN_CAST(json_value_s *) curr->value; - auto *array = json_value_as_array(value); - array_element = array->start; - } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { - auto *element = DQN_CAST(json_array_element_s *) curr->value; - array_element = element->next; - Dqn_JSON_ItPop(it); - } else { - Dqn_JSON_ItPop(it); - } - - if (obj_element) - Dqn_JSON_ItPushObjElement(it, obj_element); - else if (array_element) - Dqn_JSON_ItPushArrayElement(it, array_element); - - bool result = obj_element || array_element; - return result; -} - -// NOTE: Dqn_JSON_ItCurr /////////////////////////////////////////////////////////////////////////// -Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it) -{ - Dqn_JSONItEntry *result = nullptr; - if (!it || it->stack_count <= 0) - return result; - - result = &it->stack[it->stack_count - 1]; - return result; -} - -json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it) -{ - json_value_s *result = nullptr; - Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); - if (!curr) - return result; - - if (curr->type == Dqn_JSON_ItEntryTypeObjElement) { - auto *element = DQN_CAST(json_object_element_s *)curr->value; - result = element->value; - } else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) { - auto *element = DQN_CAST(json_array_element_s *)curr->value; - result = element->value; - } else if (curr->type == Dqn_JSON_ItEntryTypeString || - curr->type == Dqn_JSON_ItEntryTypeNumber || - curr->type == Dqn_JSON_ItEntryTypeObj || - curr->type == Dqn_JSON_ItEntryTypeArray) - { - result = DQN_CAST(json_value_s *)curr->value; - } - - return result; -} - -json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it) -{ - Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it); - auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement) - ? DQN_CAST(json_object_element_s *) curr->value - : nullptr; - return result; -} - -// NOTE: Dqn_JSON_ItValueIs //////////////////////////////////////////////////////////////////////// -json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_value_s *result = (curr && type == curr->type) ? curr : nullptr; - return result; -} - -json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_object_s *result = curr ? json_value_as_object(curr) : nullptr; - return result; -} - -json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_array_s *result = curr ? json_value_as_array(curr) : nullptr; - return result; -} - -json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_string_s *result = curr ? json_value_as_string(curr) : nullptr; - return result; -} - -json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_number_s *result = curr ? json_value_as_number(curr) : nullptr; - return result; -} - -json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr; - return result; -} - -json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it) -{ - json_value_s *curr = Dqn_JSON_ItCurrValue(it); - json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr; - return result; -} - -size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it) -{ - size_t result = 0; - if (json_array_s *curr = Dqn_JSON_ItValueIsArray(it)) - result = curr->length; - return result; -} - - -// NOTE: Dqn_JSON_ItKeyValueIs ///////////////////////////////////////////////////////////////////// -Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - Dqn_Str8 result = {}; - if (curr) { - result.data = DQN_CAST(char *)curr->name->string; - result.size = curr->name->string_size; - } - return result; -} - -bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - bool result = Dqn_JSON_String8Cmp(curr->name, key); - return result; -} - -json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_s *result = nullptr; - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = json_value_as_object(curr->value); - return result; -} - -json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_array_s *result = nullptr; - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = json_value_as_array(curr->value); - return result; -} - -json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - json_string_s *result = nullptr; - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = json_value_as_string(curr->value); - return result; -} - -json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - json_number_s *result = nullptr; - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = json_value_as_number(curr->value); - return result; -} - -json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - json_value_s *result = nullptr; - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr; - return result; -} - -json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key) -{ - json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it); - json_value_s *result = nullptr; - if (curr && Dqn_JSON_String8Cmp(curr->name, key)) - result = curr->value->type == json_type_null ? curr->value : nullptr; - return result; -} - - -// NOTE: Dqn_JSON_ItValueTo //////////////////////////////////////////////////////////////////////// -Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it) -{ - Dqn_Str8 result = {}; - if (json_string_s *curr = Dqn_JSON_ItValueIsString(it)) - result = Dqn_Str8_Init(curr->string, curr->string_size); - return result; -} - -int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it) -{ - int64_t result = {}; - if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it)) - result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value; - return result; -} - -uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it) -{ - uint64_t result = {}; - if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it)) - result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value; - return result; -} - -bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it) -{ - bool result = {}; - if (json_value_s *curr = Dqn_JSON_ItValueIsBool(it)) - result = curr->type == json_type_true; - return result; -} - -void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site) -{ - if (!it) - return; - - json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it); - if (!curr) - return; - - size_t value_type_size = 0; - char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)curr->value->type, &value_type_size); - - json_string_s const *key = curr->name; - if (it->flags & json_parse_flags_allow_location_information) { - json_string_ex_s const *info = DQN_CAST(json_string_ex_s const *)key; - Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, - call_site, - "Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]", - info->line_no, - info->row_no, - DQN_CAST(int)key->string_size, - key->string, - DQN_CAST(int)value_type_size, - value_type); - } else { - Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, - call_site, - "Unknown key-value pair in object [key=%.*s, value=%.*s]", - DQN_CAST(int)key->string_size, - key->string, - DQN_CAST(int)value_type_size, - value_type); - } -} -#endif // defined(DQN_JSON_IMPLEMENTATION) diff --git a/dqn_math.cpp b/dqn_math.cpp index f503301..534ace4 100644 --- a/dqn_math.cpp +++ b/dqn_math.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_math.h b/dqn_math.h index 8c80bf3..db71d9f 100644 --- a/dqn_math.h +++ b/dqn_math.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_os.cpp b/dqn_os.cpp index 248be80..8a592ba 100644 --- a/dqn_os.cpp +++ b/dqn_os.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -75,7 +78,7 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena) Dqn_Scratch scratch = Dqn_Scratch_Get(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)); + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators)); result = Dqn_Str8_Copy(arena, split.lhs); return result; } diff --git a/dqn_os.h b/dqn_os.h index 5b29ee6..346e02f 100644 --- a/dqn_os.h +++ b/dqn_os.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -222,17 +225,6 @@ enum Dqn_OSSemaphoreWaitResult }; #endif // !defined(DQN_NO_SEMAPHORE) -// NOTE: [$MUTX] Dqn_OSMutex /////////////////////////////////////////////////////////////////////// -struct Dqn_OSMutex -{ - #if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS) - char win32_handle[48]; - #else - pthread_mutex_t posix_handle; - pthread_mutexattr_t posix_attribs; - #endif -}; - // NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// #if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE) typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*); @@ -295,6 +287,7 @@ DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow (); DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':'); DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 (Dqn_OSDateTime time, char date_separator = '-', char hms_separator = ':'); DQN_API uint64_t Dqn_OS_DateUnixTime (); +DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate (uint64_t time); DQN_API uint64_t Dqn_OS_DateToUnixTime (Dqn_OSDateTime date); DQN_API bool Dqn_OS_DateIsValid (Dqn_OSDateTime date); @@ -378,10 +371,10 @@ DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait (Dqn_OSSemaphore *sem #endif // !defined(DQN_NO_SEMAPHORE) // NOTE: [$MUTX] Dqn_OSMutex /////////////////////////////////////////////////////////////////////// -DQN_API Dqn_OSMutex Dqn_OS_MutexInit (uint32_t initial_count, uint32_t max_count); +DQN_API Dqn_OSMutex Dqn_OS_MutexInit (); DQN_API void Dqn_OS_MutexDeinit(Dqn_OSMutex *mutex); -DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex mutex); -DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex mutex); +DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex *mutex); +DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex); // NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// #if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE) diff --git a/dqn_os_posix.cpp b/dqn_os_posix.cpp index db12199..5a7d30b 100644 --- a/dqn_os_posix.cpp +++ b/dqn_os_posix.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -137,6 +140,20 @@ DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date) return result; } +DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate(uint64_t time) +{ + time_t posix_time = DQN_CAST(time_t) time; + struct tm posix_date = *gmtime(&posix_time); + Dqn_OSDateTime result = {}; + result.year = posix_date.tm_year + 1900; + result.month = posix_date.tm_mon + 1; + result.day = posix_date.tm_mday; + result.hour = posix_date.tm_hour; + result.minutes = posix_date.tm_min; + result.seconds = posix_date.tm_sec; + return result; +} + DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size) { #if defined(DQN_PLATFORM_EMSCRIPTEN) diff --git a/dqn_os_win32.cpp b/dqn_os_win32.cpp index c365b0f..58ac16c 100644 --- a/dqn_os_win32.cpp +++ b/dqn_os_win32.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ @@ -127,6 +130,30 @@ DQN_API uint64_t Dqn_OS_DateUnixTime() return result; } +DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate(uint64_t time) +{ + // NOTE: Windows epoch time starts from Jan 1, 1601 and counts in + // 100-nanoseconds intervals. + // + // See: https://devblogs.microsoft.com/oldnewthing/20090306-00/?p=18913 + + uint64_t win32_time = 116'444'736'000'000'000 + (time * 10'000'000); + SYSTEMTIME sys_time = {}; + FILETIME file_time = {}; + file_time.dwLowDateTime = (DWORD)win32_time; + file_time.dwHighDateTime = win32_time >> 32; + FileTimeToSystemTime(&file_time, &sys_time); + + Dqn_OSDateTime result = {}; + result.year = DQN_CAST(uint16_t)sys_time.wYear; + result.month = DQN_CAST(uint8_t)sys_time.wMonth; + result.day = DQN_CAST(uint8_t)sys_time.wDay; + result.hour = DQN_CAST(uint8_t)sys_time.wHour; + result.minutes = DQN_CAST(uint8_t)sys_time.wMinute; + result.seconds = DQN_CAST(uint8_t)sys_time.wSecond; + return result; +} + DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date) { DQN_ASSERT(Dqn_OS_DateIsValid(date)); diff --git a/dqn_os_win32.h b/dqn_os_win32.h index 7a0ef98..e06b121 100644 --- a/dqn_os_win32.h +++ b/dqn_os_win32.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ diff --git a/dqn_string.cpp b/dqn_string.cpp index a6eaee3..e39485b 100644 --- a/dqn_string.cpp +++ b/dqn_string.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -156,7 +159,7 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8 return result; } -DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) { Dqn_Str8BinarySplitResult result = {}; if (!Dqn_Str8_HasData(string) || !find || find_size == 0) @@ -179,9 +182,9 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 stri return result; } -DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, Dqn_Str8 find) +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast(Dqn_Str8 string, Dqn_Str8 find) { - Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitReverseArray(string, &find, 1); + Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitLastArray(string, &find, 1); return result; } @@ -218,7 +221,7 @@ DQN_API Dqn_Slice Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin return result; } -DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) +DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size) { Dqn_Str8FindResult result = {}; if (!Dqn_Str8_HasData(string) || !find || find_size == 0) @@ -241,13 +244,13 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_St return result; } -DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString(Dqn_Str8 string, Dqn_Str8 find) +DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find) { - Dqn_Str8FindResult result = Dqn_Str8_FindFirstStringArray(string, &find, 1); + Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1); return result; } -DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst(Dqn_Str8 string, uint32_t flags) +DQN_API Dqn_Str8FindResult Dqn_Str8_Find(Dqn_Str8 string, uint32_t flags) { Dqn_Str8FindResult result = {}; for (size_t index = 0; !result.found && index < string.size; index++) { @@ -457,7 +460,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string) DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path) { Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; - Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators)); + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(path, separators, DQN_ARRAY_UCOUNT(separators)); Dqn_Str8 result = Dqn_Str8_HasData(split.rhs) ? split.rhs : split.lhs; return result; } @@ -471,14 +474,14 @@ DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension(Dqn_Str8 path) DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path) { - Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8(".")); + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLast(path, DQN_STR8(".")); Dqn_Str8 result = split.lhs; return result; } DQN_API Dqn_Str8 Dqn_Str8_FileExtension(Dqn_Str8 path) { - Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8(".")); + Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLast(path, DQN_STR8(".")); Dqn_Str8 result = split.rhs; return result; } @@ -895,17 +898,18 @@ DQN_API bool Dqn_Char_IsHex(char ch) return result; } -DQN_API uint8_t Dqn_Char_HexToU8(char ch) +DQN_API Dqn_CharHexToU8 Dqn_Char_HexToU8(char ch) { - DQN_ASSERTF(Dqn_Char_IsHex(ch), "Hex character not valid '%c'", ch); - - uint8_t result = 0; + Dqn_CharHexToU8 result = {}; + result.success = true; if (ch >= 'a' && ch <= 'f') - result = ch - 'a' + 10; + result.value = ch - 'a' + 10; else if (ch >= 'A' && ch <= 'F') - result = ch - 'A' + 10; + result.value = ch - 'A' + 10; + else if (ch >= '0' && ch <= '9') + result.value = ch - '0'; else - result = ch - '0'; + result.success = false; return result; } diff --git a/dqn_string.h b/dqn_string.h index 605719d..ce6d4de 100644 --- a/dqn_string.h +++ b/dqn_string.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -159,14 +162,14 @@ DQN_API Dqn_Str8 Dqn_Str8_Advance DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string); 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_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); +DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (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_Str8SplitIncludeEmptyStrings mode); DQN_API Dqn_Slice Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode); -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_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size); +DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find); +DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags); DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char); @@ -234,12 +237,18 @@ template bool operator!= #endif // !defined(DQN_NO_FSTR8) // NOTE: [$CHAR] Dqn_Char ////////////////////////////////////////////////////////////////////////// +struct Dqn_CharHexToU8 +{ + bool success; + uint8_t value; +}; + DQN_API bool Dqn_Char_IsAlphabet (char ch); DQN_API bool Dqn_Char_IsDigit (char ch); DQN_API bool Dqn_Char_IsAlphaNum (char ch); DQN_API bool Dqn_Char_IsWhitespace (char ch); DQN_API bool Dqn_Char_IsHex (char ch); -DQN_API uint8_t Dqn_Char_HexToU8 (char ch); +DQN_API Dqn_CharHexToU8 Dqn_Char_HexToU8 (char ch); DQN_API char Dqn_Char_ToHex (char ch); DQN_API char Dqn_Char_ToHexUnchecked (char ch); DQN_API char Dqn_Char_ToLower (char ch); diff --git a/dqn_thread_context.cpp b/dqn_thread_context.cpp index 54af773..15ddf2c 100644 --- a/dqn_thread_context.cpp +++ b/dqn_thread_context.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -58,6 +61,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get() DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()"); // NOTE: Setup scratch arenas ////////////////////////////////////////////////////////////////// + Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex); DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) { // NOTE: We allocate arenas so that they all come from the memory @@ -100,7 +104,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get() } result->error_sink.arena = result->error_sink_arena; } - + Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex); return result; } diff --git a/dqn_thread_context.h b/dqn_thread_context.h index 5b12bd3..855913f 100644 --- a/dqn_thread_context.h +++ b/dqn_thread_context.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_type_info.cpp b/dqn_type_info.cpp index e346dc2..f5911c8 100644 --- a/dqn_type_info.cpp +++ b/dqn_type_info.cpp @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/dqn_type_info.h b/dqn_type_info.h index 7467f79..11a891c 100644 --- a/dqn_type_info.h +++ b/dqn_type_info.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + /* //////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -30,6 +33,7 @@ struct Dqn_TypeField Dqn_isize value; Dqn_usize offset_of; Dqn_usize size_of; + Dqn_usize align_of; Dqn_Str8 type_decl; uint32_t type_enum; bool is_pointer; diff --git a/dqn_unit_tests.cpp b/dqn_unit_tests.cpp index 12cb5ec..a869934 100644 --- a/dqn_unit_tests.cpp +++ b/dqn_unit_tests.cpp @@ -3,6 +3,333 @@ #include +#if !defined(__clang__) +// NOTE: Taken from MSDN __cpuid example implementation +// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170 +#include +#include +#include +#include + +class Dqn_RefImplCPUReport +{ + // forward declarations + class Dqn_RefImplCPUReport_Internal; + +public: + // getters + static std::string Vendor(void) { return CPU_Rep.vendor_; } + static std::string Brand(void) { return CPU_Rep.brand_; } + + static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; } + static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; } + static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; } + static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; } + static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; } + static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; } + static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; } + static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; } + static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; } + static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; } + static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; } + static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; } + static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; } + static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; } + static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; } + static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; } + + static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; } + static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; } + static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; } + static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; } + static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; } + static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; } + static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; } + static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; } + static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; } + + static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; } + static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; } + static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; } + static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; } + static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; } + static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; } + static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; } + static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; } + static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; } + static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; } + static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; } + static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; } + static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; } + static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; } + static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; } + + static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; } + + static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; } + static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; } + static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; } + static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; } + static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; } + static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; } + + static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; } + static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; } + static bool RDTSCP(void) { return CPU_Rep.f_81_EDX_[27]; } + static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; } + static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } + +private: + static const Dqn_RefImplCPUReport_Internal CPU_Rep; + + class Dqn_RefImplCPUReport_Internal + { + public: + Dqn_RefImplCPUReport_Internal() + : nIds_{ 0 }, + nExIds_{ 0 }, + isIntel_{ false }, + isAMD_{ false }, + f_1_ECX_{ 0 }, + f_1_EDX_{ 0 }, + f_7_EBX_{ 0 }, + f_7_ECX_{ 0 }, + f_81_ECX_{ 0 }, + f_81_EDX_{ 0 }, + data_{}, + extdata_{} + { + //int cpuInfo[4] = {-1}; + std::array cpui; + + // Calling __cpuid with 0x0 as the function_id argument + // gets the number of the highest valid function ID. + __cpuid(cpui.data(), 0); + nIds_ = cpui[0]; + + for (int i = 0; i <= nIds_; ++i) + { + __cpuidex(cpui.data(), i, 0); + data_.push_back(cpui); + } + + // Capture vendor string + char vendor[0x20]; + memset(vendor, 0, sizeof(vendor)); + *reinterpret_cast(vendor) = data_[0][1]; + *reinterpret_cast(vendor + 4) = data_[0][3]; + *reinterpret_cast(vendor + 8) = data_[0][2]; + vendor_ = vendor; + if (vendor_ == "GenuineIntel") + { + isIntel_ = true; + } + else if (vendor_ == "AuthenticAMD") + { + isAMD_ = true; + } + + // load bitset with flags for function 0x00000001 + if (nIds_ >= 1) + { + f_1_ECX_ = data_[1][2]; + f_1_EDX_ = data_[1][3]; + } + + // load bitset with flags for function 0x00000007 + if (nIds_ >= 7) + { + f_7_EBX_ = data_[7][1]; + f_7_ECX_ = data_[7][2]; + } + + // Calling __cpuid with 0x80000000 as the function_id argument + // gets the number of the highest valid extended ID. + __cpuid(cpui.data(), 0x80000000); + nExIds_ = cpui[0]; + + char brand[0x40]; + memset(brand, 0, sizeof(brand)); + + for (int i = 0x80000000; i <= nExIds_; ++i) + { + __cpuidex(cpui.data(), i, 0); + extdata_.push_back(cpui); + } + + // load bitset with flags for function 0x80000001 + if (nExIds_ >= 0x80000001) + { + f_81_ECX_ = extdata_[1][2]; + f_81_EDX_ = extdata_[1][3]; + } + + // Interpret CPU brand string if reported + if (nExIds_ >= 0x80000004) + { + memcpy(brand, extdata_[2].data(), sizeof(cpui)); + memcpy(brand + 16, extdata_[3].data(), sizeof(cpui)); + memcpy(brand + 32, extdata_[4].data(), sizeof(cpui)); + brand_ = brand; + } + }; + + int nIds_; + int nExIds_; + std::string vendor_; + std::string brand_; + bool isIntel_; + bool isAMD_; + std::bitset<32> f_1_ECX_; + std::bitset<32> f_1_EDX_; + std::bitset<32> f_7_EBX_; + std::bitset<32> f_7_ECX_; + std::bitset<32> f_81_ECX_; + std::bitset<32> f_81_EDX_; + std::vector> data_; + std::vector> extdata_; + }; +}; + +// Initialize static member data +const Dqn_RefImplCPUReport::Dqn_RefImplCPUReport_Internal Dqn_RefImplCPUReport::CPU_Rep; +#endif // !defined(__clang__) + +#if 0 +static void Dqn_RefImpl_CPUReportDump() // Print out supported instruction set features +{ + auto support_message = [](std::string isa_feature, bool is_supported) { + printf("%s %s\n", isa_feature.c_str(), is_supported ? "supported" : "not supported"); + }; + + printf("%s\n", Dqn_RefImplCPUReport::Vendor().c_str()); + printf("%s\n", Dqn_RefImplCPUReport::Brand().c_str()); + + support_message("3DNOW", Dqn_RefImplCPUReport::_3DNOW()); + support_message("3DNOWEXT", Dqn_RefImplCPUReport::_3DNOWEXT()); + support_message("ABM", Dqn_RefImplCPUReport::ABM()); + support_message("ADX", Dqn_RefImplCPUReport::ADX()); + support_message("AES", Dqn_RefImplCPUReport::AES()); + support_message("AVX", Dqn_RefImplCPUReport::AVX()); + support_message("AVX2", Dqn_RefImplCPUReport::AVX2()); + support_message("AVX512CD", Dqn_RefImplCPUReport::AVX512CD()); + support_message("AVX512ER", Dqn_RefImplCPUReport::AVX512ER()); + support_message("AVX512F", Dqn_RefImplCPUReport::AVX512F()); + support_message("AVX512PF", Dqn_RefImplCPUReport::AVX512PF()); + support_message("BMI1", Dqn_RefImplCPUReport::BMI1()); + support_message("BMI2", Dqn_RefImplCPUReport::BMI2()); + support_message("CLFSH", Dqn_RefImplCPUReport::CLFSH()); + support_message("CMPXCHG16B", Dqn_RefImplCPUReport::CMPXCHG16B()); + support_message("CX8", Dqn_RefImplCPUReport::CX8()); + support_message("ERMS", Dqn_RefImplCPUReport::ERMS()); + support_message("F16C", Dqn_RefImplCPUReport::F16C()); + support_message("FMA", Dqn_RefImplCPUReport::FMA()); + support_message("FSGSBASE", Dqn_RefImplCPUReport::FSGSBASE()); + support_message("FXSR", Dqn_RefImplCPUReport::FXSR()); + support_message("HLE", Dqn_RefImplCPUReport::HLE()); + support_message("INVPCID", Dqn_RefImplCPUReport::INVPCID()); + support_message("LAHF", Dqn_RefImplCPUReport::LAHF()); + support_message("LZCNT", Dqn_RefImplCPUReport::LZCNT()); + support_message("MMX", Dqn_RefImplCPUReport::MMX()); + support_message("MMXEXT", Dqn_RefImplCPUReport::MMXEXT()); + support_message("MONITOR", Dqn_RefImplCPUReport::MONITOR()); + support_message("MOVBE", Dqn_RefImplCPUReport::MOVBE()); + support_message("MSR", Dqn_RefImplCPUReport::MSR()); + support_message("OSXSAVE", Dqn_RefImplCPUReport::OSXSAVE()); + support_message("PCLMULQDQ", Dqn_RefImplCPUReport::PCLMULQDQ()); + support_message("POPCNT", Dqn_RefImplCPUReport::POPCNT()); + support_message("PREFETCHWT1", Dqn_RefImplCPUReport::PREFETCHWT1()); + support_message("RDRAND", Dqn_RefImplCPUReport::RDRAND()); + support_message("RDSEED", Dqn_RefImplCPUReport::RDSEED()); + support_message("RDTSCP", Dqn_RefImplCPUReport::RDTSCP()); + support_message("RTM", Dqn_RefImplCPUReport::RTM()); + support_message("SEP", Dqn_RefImplCPUReport::SEP()); + support_message("SHA", Dqn_RefImplCPUReport::SHA()); + support_message("SSE", Dqn_RefImplCPUReport::SSE()); + support_message("SSE2", Dqn_RefImplCPUReport::SSE2()); + support_message("SSE3", Dqn_RefImplCPUReport::SSE3()); + support_message("SSE4.1", Dqn_RefImplCPUReport::SSE41()); + support_message("SSE4.2", Dqn_RefImplCPUReport::SSE42()); + support_message("SSE4a", Dqn_RefImplCPUReport::SSE4a()); + support_message("SSSE3", Dqn_RefImplCPUReport::SSSE3()); + support_message("SYSCALL", Dqn_RefImplCPUReport::SYSCALL()); + support_message("TBM", Dqn_RefImplCPUReport::TBM()); + support_message("XOP", Dqn_RefImplCPUReport::XOP()); + support_message("XSAVE", Dqn_RefImplCPUReport::XSAVE()); +}; +#endif + +static Dqn_UTest Dqn_Test_Base() +{ + Dqn_UTest test = {}; + DQN_UTEST_GROUP(test, "Dqn_Base") { + // TODO(doyle): cpuid refimpl doesn't work on clang + #if !defined(__clang__) + DQN_UTEST_TEST("Query CPUID") { + Dqn_CPUReport cpu_report = Dqn_CPU_Report(); + + // NOTE: Sanity check our report against MSDN's example //////////////////////////////////////// + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_3DNow) == Dqn_RefImplCPUReport::_3DNOW()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_3DNowExt) == Dqn_RefImplCPUReport::_3DNOWEXT()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_ABM) == Dqn_RefImplCPUReport::ABM()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AES) == Dqn_RefImplCPUReport::AES()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX) == Dqn_RefImplCPUReport::AVX()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX2) == Dqn_RefImplCPUReport::AVX2()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX512CD) == Dqn_RefImplCPUReport::AVX512CD()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX512ER) == Dqn_RefImplCPUReport::AVX512ER()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX512F) == Dqn_RefImplCPUReport::AVX512F()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_AVX512PF) == Dqn_RefImplCPUReport::AVX512PF()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_CMPXCHG16B) == Dqn_RefImplCPUReport::CMPXCHG16B()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_F16C) == Dqn_RefImplCPUReport::F16C()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_FMA) == Dqn_RefImplCPUReport::FMA()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_MMX) == Dqn_RefImplCPUReport::MMX()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_MmxExt) == Dqn_RefImplCPUReport::MMXEXT()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_MONITOR) == Dqn_RefImplCPUReport::MONITOR()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_MOVBE) == Dqn_RefImplCPUReport::MOVBE()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_PCLMULQDQ) == Dqn_RefImplCPUReport::PCLMULQDQ()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_POPCNT) == Dqn_RefImplCPUReport::POPCNT()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_RDRAND) == Dqn_RefImplCPUReport::RDRAND()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_RDSEED) == Dqn_RefImplCPUReport::RDSEED()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_RDTSCP) == Dqn_RefImplCPUReport::RDTSCP()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SHA) == Dqn_RefImplCPUReport::SHA()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE) == Dqn_RefImplCPUReport::SSE()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE2) == Dqn_RefImplCPUReport::SSE2()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE3) == Dqn_RefImplCPUReport::SSE3()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE41) == Dqn_RefImplCPUReport::SSE41()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE42) == Dqn_RefImplCPUReport::SSE42()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSE4A) == Dqn_RefImplCPUReport::SSE4a()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SSSE3) == Dqn_RefImplCPUReport::SSSE3()); + + // NOTE: Feature flags we haven't bothered detecting yet but are in MSDN's example ///////////// + #if 0 + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_ADX) == Dqn_RefImplCPUReport::ADX()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_BMI1) == Dqn_RefImplCPUReport::BMI1()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_BMI2) == Dqn_RefImplCPUReport::BMI2()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_CLFSH) == Dqn_RefImplCPUReport::CLFSH()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_CX8) == Dqn_RefImplCPUReport::CX8()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_ERMS) == Dqn_RefImplCPUReport::ERMS()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_FSGSBASE) == Dqn_RefImplCPUReport::FSGSBASE()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_FXSR) == Dqn_RefImplCPUReport::FXSR()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_HLE) == Dqn_RefImplCPUReport::HLE()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_INVPCID) == Dqn_RefImplCPUReport::INVPCID()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_LAHF) == Dqn_RefImplCPUReport::LAHF()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_LZCNT) == Dqn_RefImplCPUReport::LZCNT()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_MSR) == Dqn_RefImplCPUReport::MSR()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_OSXSAVE) == Dqn_RefImplCPUReport::OSXSAVE()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_PREFETCHWT1) == Dqn_RefImplCPUReport::PREFETCHWT1()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_RTM) == Dqn_RefImplCPUReport::RTM()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SEP) == Dqn_RefImplCPUReport::SEP()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_SYSCALL) == Dqn_RefImplCPUReport::SYSCALL()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_TBM) == Dqn_RefImplCPUReport::TBM()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XOP) == Dqn_RefImplCPUReport::XOP()); + DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XSAVE) == Dqn_RefImplCPUReport::XSAVE()); + #endif + } + #endif // !defined(__clang__) + } + return test; +} + static Dqn_UTest Dqn_Test_Arena() { Dqn_UTest test = {}; @@ -104,79 +431,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_STR8("0x123")); + uint64_t result = Dqn_HexToU64(DQN_STR8("0x123")); DQN_UTEST_ASSERTF(&test, result == 0x123, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert 0xFFFF") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xFFFF")); + uint64_t result = Dqn_HexToU64(DQN_STR8("0xFFFF")); DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert FFFF") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("FFFF")); + uint64_t result = Dqn_HexToU64(DQN_STR8("FFFF")); DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert abCD") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("abCD")); + uint64_t result = Dqn_HexToU64(DQN_STR8("abCD")); DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert 0xabCD") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xabCD")); + uint64_t result = Dqn_HexToU64(DQN_STR8("0xabCD")); DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert 0x") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0x")); + uint64_t result = Dqn_HexToU64(DQN_STR8("0x")); DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert 0X") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0X")); + uint64_t result = Dqn_HexToU64(DQN_STR8("0X")); DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert 3") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("3")); + uint64_t result = Dqn_HexToU64(DQN_STR8("3")); DQN_UTEST_ASSERTF(&test, result == 3, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert f") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("f")); + uint64_t result = Dqn_HexToU64(DQN_STR8("f")); DQN_UTEST_ASSERTF(&test, result == 0xf, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert g") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("g")); + uint64_t result = Dqn_HexToU64(DQN_STR8("g")); DQN_UTEST_ASSERTF(&test, result == 0, "result: %" PRIu64, result); } DQN_UTEST_TEST("Convert -0x3") { - uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("-0x3")); + uint64_t result = Dqn_HexToU64(DQN_STR8("-0x3")); DQN_UTEST_ASSERTF(&test, result == 0, "result: %" PRIu64, result); } uint32_t number = 0xd095f6; DQN_UTEST_TEST("Convert %x to string", number) { - Dqn_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); + Dqn_Str8 number_hex = Dqn_BytesToHex(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_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number)); + Dqn_Str8 number_hex = Dqn_BytesToHex(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_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_Str8 bytes = Dqn_HexToBytes(scratch.arena, hex); DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")), "number_hex=%.*s", - DQN_STR_FMT(Dqn_Bin_BytesToHexArena(scratch.arena, bytes.data, bytes.size))); + DQN_STR_FMT(Dqn_BytesToHex(scratch.arena, bytes.data, bytes.size))); } } @@ -975,7 +1302,7 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] = void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 input_hex = Dqn_Bin_BytesToHexArena(scratch.arena, input.data, input.size); + Dqn_Str8 input_hex = Dqn_BytesToHex(scratch.arena, input.data, input.size); switch(hash_type) { @@ -1619,12 +1946,11 @@ static Dqn_UTest Dqn_Test_Str8() DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %" PRIu64, result.value); } - // NOTE: Dqn_Str8_Find - // ========================================================================================= + // NOTE: Dqn_Str8_Find ///////////////////////////////////////////////////////////////////// DQN_UTEST_TEST("Find: String (char) is not in buffer") { Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 find = DQN_STR8("2"); - Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find); + Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find); DQN_UTEST_ASSERT(&test, !result.found); DQN_UTEST_ASSERT(&test, result.index == 0); DQN_UTEST_ASSERT(&test, result.match.data == nullptr); @@ -1634,14 +1960,13 @@ static Dqn_UTest Dqn_Test_Str8() DQN_UTEST_TEST("Find: String (char) is in buffer") { Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 find = DQN_STR8("6"); - Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find); + Dqn_Str8FindResult result = Dqn_Str8_FindStr8(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_Str8_FileNameFromPath - // ========================================================================================= + // NOTE: Dqn_Str8_FileNameFromPath ///////////////////////////////////////////////////////// DQN_UTEST_TEST("File name from Windows path") { Dqn_Str8 buf = DQN_STR8("C:\\ABC\\test.exe"); Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf); @@ -1897,6 +2222,7 @@ void Dqn_Test_RunSuite() { Dqn_UTest tests[] = { + Dqn_Test_Base(), Dqn_Test_Arena(), Dqn_Test_Bin(), Dqn_Test_BinarySearch(), @@ -1933,7 +2259,7 @@ void Dqn_Test_RunSuite() int main(int argc, char *argv[]) { (void)argv; (void)argc; - Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures); + Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures); Dqn_Test_RunSuite(); return 0; } diff --git a/dqn_win32.h b/dqn_win32.h index f7ca625..46df8dd 100644 --- a/dqn_win32.h +++ b/dqn_win32.h @@ -1,3 +1,6 @@ +#pragma once +#include "dqn.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ diff --git a/readme.md b/readme.md index 3959d26..a429e70 100644 --- a/readme.md +++ b/readme.md @@ -9,21 +9,8 @@ separated by category into files for organisation. You only need to include ## Build To build with this library, copy all the `*.[h|cpp]` files at the root of the -repository to your desired location, accessible by your project and in one -header file include the header. - -```cpp -#include "dqn.h" -``` - -`dqn.h` includes all other files and their declaration into your header. In -*one* `.cpp` file define the macro to enable the implementation of the header in -that translation unit. - -```cpp -#define DQN_IMPLEMENTATION -#include "dqn.h" -``` +repository to your desired location and compile `dqn.cpp` or include it into one +of your translation units. Finally ensure that the compiler has in its search paths for the include directory where headers are located, e.g. `-I `.