Pull in bug fixes from upstream

This commit is contained in:
doylet 2024-04-18 22:59:11 +10:00
parent 243a214b43
commit 765b8255f7
39 changed files with 2055 additions and 882 deletions

View File

@ -13,7 +13,7 @@ pushd Build
REM O2 Optimisation Level 2 REM O2 Optimisation Level 2
REM Oi Use CPU Intrinsics REM Oi Use CPU Intrinsics
REM Z7 Combine multi-debug files to one debug file 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 set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo

View File

@ -11,10 +11,9 @@ pushd Build
-Werror \ -Werror \
-fsanitize=address \ -fsanitize=address \
-std=c++17 \ -std=c++17 \
-D DQN_IMPLEMENTATION \
-D DQN_UNIT_TESTS_WITH_MAIN \ -D DQN_UNIT_TESTS_WITH_MAIN \
-D DQN_UNIT_TESTS_WITH_KECCAK \ -D DQN_UNIT_TESTS_WITH_KECCAK \
-x c++ ${code_dir}/dqn.h \ -x ${code_dir}/dqn.cpp \
-g \ -g \
-o dqn_unit_tests -o dqn_unit_tests
popd popd

62
dqn.cpp Normal file
View File

@ -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: '<function>': 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"

79
dqn.h
View File

@ -1,4 +1,4 @@
#if !defined(DQN_H) #pragma once
#define DQN_H #define DQN_H
/* /*
@ -15,6 +15,7 @@
// \___| // \___|
// //
// dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dqn // dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dqn
// ASCII -- BigMoney-NW by Nathan Bloomfild
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -42,11 +43,7 @@
// //
// -- Compiling -- // -- Compiling --
// //
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to // Compile dqn.cpp or include it into one of your translation units.
// enable the implementation of this library, for example:
//
// #define DQN_IMEPLEMENTATION
// #include "dqn.h"
// //
// Additionally, this library supports including/excluding specific sections // Additionally, this library supports including/excluding specific sections
// of the library by using #define on the name of the section. These names are // 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 from being included. This might be useful if you are including the
// library in your project yourself. The library must still be defined and // library in your project yourself. The library must still be defined and
// visible before this header. // 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) || \ #if defined(DQN_ONLY_VARRAY) || \
@ -343,65 +348,3 @@
#endif #endif
#include "dqn_json.h" #include "dqn_json.h"
#endif #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: '<function>': 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

View File

@ -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 // NOTE: Store the offset to the original pointer behind the user's
// pointer. // 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 // 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_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, Dqn_Str8 label)
{ {
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_ArenaCatalogItem *result = &catalog->sentinel; Dqn_ArenaCatalogItem *result = &catalog->sentinel;
for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) { for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
if (item->label == label) { if (item->label == label) {
@ -434,6 +436,7 @@ DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, D
break; break;
} }
} }
Dqn_TicketMutex_End(&catalog->ticket_mutex);
return result; 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_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_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy); 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_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
va_end(args); va_end(args);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); 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_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_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
Dqn_TicketMutex_End(&catalog->ticket_mutex);
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label); 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_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_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); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy);
return result; 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_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_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); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
return result; return result;
} }
@ -510,7 +523,9 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args); 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); Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
va_end(args); va_end(args);
return result; return result;

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -51,6 +54,7 @@ struct Dqn_Arena
{ {
Dqn_ArenaBlock *curr; Dqn_ArenaBlock *curr;
uint8_t flags; uint8_t flags;
Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use.
}; };
struct Dqn_ArenaTempMem struct Dqn_ArenaTempMem

286
dqn_avx512f.cpp Normal file
View File

@ -0,0 +1,286 @@
#pragma once
#include "dqn.h"
#include <immintrin.h>
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// /$$$$$$ /$$ /$$ /$$ /$$ /$$$$$$$ /$$ /$$$$$$ /$$$$$$$$
// /$$__ $$| $$ | $$| $$ / $$ | $$____/ /$$$$ /$$__ $$| $$_____/
// | $$ \ $$| $$ | $$| $$/ $$/ | $$ |_ $$ |__/ \ $$| $$
// | $$$$$$$$| $$ / $$/ \ $$$$/ /$$$$$$| $$$$$$$ | $$ /$$$$$$/| $$$$$
// | $$__ $$ \ $$ $$/ >$$ $$|______/|_____ $$ | $$ /$$____/ | $$__/
// | $$ | $$ \ $$$/ /$$/\ $$ /$$ \ $$ | $$ | $$ | $$
// | $$ | $$ \ $/ | $$ \ $$ | $$$$$$//$$$$$$| $$$$$$$$| $$
// |__/ |__/ \_/ |__/ |__/ \______/|______/|________/|__/
//
// 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> Dqn_Str8_SplitAllocAVX512F(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode)
{
Dqn_Slice<Dqn_Str8> 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;
}

30
dqn_avx512f.h Normal file
View File

@ -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> Dqn_Str8_SplitAllocAVX512F (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
#endif // DQN_AVX512F_H

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -21,16 +24,196 @@
#include <cpuid.h> #include <cpuid.h>
#endif #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 = {}; Dqn_CPUIDResult result = {};
#if defined(DQN_COMPILER_MSVC) __cpuidex(result.values, args.eax, args.ecx);
__cpuid(DQN_CAST(int *)result.array, function_id); return result;
#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 DQN_API Dqn_usize Dqn_CPU_HasFeatureArray(Dqn_CPUReport const *report, Dqn_CPUFeatureQuery *features, Dqn_usize features_size)
#error "Compiler not supported" {
#endif 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; return result;
} }
#endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN) #endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)

View File

@ -73,6 +73,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <limits.h> #include <limits.h>
#include <inttypes.h> // PRIu64...
#if !defined(DQN_OS_WIN32) #if !defined(DQN_OS_WIN32)
#include <stdlib.h> // exit() #include <stdlib.h> // exit()
@ -455,10 +456,110 @@ struct Dqn_ErrorSink
#endif #endif
#if !defined(DQN_PLATFORM_ARM64) #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 #endif // DQN_PLATFORM_ARM64
// NOTE: [$TMUT] Dqn_TicketMutex /////////////////////////////////////////////////////////////////// // 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 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 ///////////////////////////////////////////////////////////////////////// // NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
enum Dqn_PrintStd 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 uint64_t Dqn_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value);
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value); DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value);
#if !defined(DQN_PLATFORM_ARM64) #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 <Dqn_usize N> 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 #endif
// NOTE: [$TMUT] Dqn_TicketMutex /////////////////////////////////////////////////////////////////// // NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
@ -671,3 +789,9 @@ DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
#error Unsupported compiler #error Unsupported compiler
#endif #endif
} }
template <Dqn_usize N> bool Dqn_CPU_HasAllFeaturesCArray(Dqn_CPUReport const *report, Dqn_CPUFeature const (&features)[N])
{
bool result = Dqn_CPU_HasAllFeatures(report, features, N);
return result;
}

View File

@ -45,6 +45,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] =
Dqn_CGenTableHeaderType_CppType, Dqn_CGenTableHeaderType_CppType,
Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppName,
Dqn_CGenTableHeaderType_CppIsPtr, Dqn_CGenTableHeaderType_CppIsPtr,
Dqn_CGenTableHeaderType_CppOpEquals,
Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySize,
Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_CppArraySizeField,
Dqn_CGenTableHeaderType_GenTypeInfo, 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_CppName: result = DQN_STR8("cpp_name"); break;
case Dqn_CGenTableHeaderType_CppValue: result = DQN_STR8("cpp_value"); 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_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_CppArraySize: result = DQN_STR8("cpp_array_size"); break;
case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break; case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break;
case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); 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; } 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) { 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_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size);
Dqn_CppLine(cpp, 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, row_index,
DQN_STR_FMT(cpp_name.column.string), DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "", 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), DQN_STR_FMT(cpp_name.column.string),
cpp_name_padding, "", cpp_name_padding, "",
// NOTE: alignof(a->b)
DQN_STR_FMT(cpp_type.column.string),
cpp_type_padding, "",
// NOTE: Type string // NOTE: Type string
DQN_STR_FMT(cpp_type.column.string), DQN_STR_FMT(cpp_type.column.string),
cpp_type_padding, "", 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_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_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, 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, row_index,
// NOTE: Name string // NOTE: Name string
@ -739,6 +785,9 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
// NOTE: sizeof(a) // NOTE: sizeof(a)
DQN_STR_FMT(struct_or_enum_name), DQN_STR_FMT(struct_or_enum_name),
// NOTE: alignof(a)
DQN_STR_FMT(struct_or_enum_name),
// NOTE: ..._Type_... // NOTE: ..._Type_...
DQN_STR_FMT(emit_prefix), DQN_STR_FMT(emit_prefix),
DQN_STR_FMT(struct_or_enum_name)); 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) { for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
if (table->type != Dqn_CGenTableType_CodeGenEnum) if (table->type != Dqn_CGenTableType_CodeGenEnum)
continue; continue;
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; 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_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_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)) { 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;");
}
}
}
} }
} }

View File

@ -67,6 +67,7 @@ enum Dqn_CGenTableHeaderType
Dqn_CGenTableHeaderType_CppName, Dqn_CGenTableHeaderType_CppName,
Dqn_CGenTableHeaderType_CppValue, Dqn_CGenTableHeaderType_CppValue,
Dqn_CGenTableHeaderType_CppIsPtr, Dqn_CGenTableHeaderType_CppIsPtr,
Dqn_CGenTableHeaderType_CppOpEquals,
Dqn_CGenTableHeaderType_CppArraySize, Dqn_CGenTableHeaderType_CppArraySize,
Dqn_CGenTableHeaderType_CppArraySizeField, Dqn_CGenTableHeaderType_CppArraySizeField,
Dqn_CGenTableHeaderType_GenTypeInfo, Dqn_CGenTableHeaderType_GenTypeInfo,

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -281,6 +284,8 @@ template <typename T, Dqn_usize N> void Dqn_FArra
#define DQN_TO_SLICE(val) Dqn_Slice_Init((val)->data, (val)->size) #define DQN_TO_SLICE(val) Dqn_Slice_Init((val)->data, (val)->size)
template <typename T> Dqn_Slice<T> Dqn_Slice_Init (T* const data, Dqn_usize size); template <typename T> Dqn_Slice<T> Dqn_Slice_Init (T* const data, Dqn_usize size);
template <typename T, Dqn_usize N> Dqn_Slice<T> Dqn_Slice_InitCArray (Dqn_Arena *arena, T const (&array)[N]); template <typename T, Dqn_usize N> Dqn_Slice<T> Dqn_Slice_InitCArray (Dqn_Arena *arena, T const (&array)[N]);
template <typename T> Dqn_Slice<T> Dqn_Slice_Copy (Dqn_Arena *arena, Dqn_Slice<T> slice);
template <typename T> Dqn_Slice<T> Dqn_Slice_CopyPtr (Dqn_Arena *arena, T* const data, Dqn_usize size);
template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem); template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator); Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator);
Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array); Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array);
@ -622,7 +627,7 @@ template <typename T> bool Dqn_VArray_Reserve(Dqn_VArray<T> *array, Dqn_usize co
#endif // !defined(DQN_NO_VARRAY) #endif // !defined(DQN_NO_VARRAY)
#if !defined(DQN_NO_SARRAY) #if !defined(DQN_NO_SARRAY)
// NOTE: [$FARR] Dqn_SArray //////////////////////////////////////////////////////////////////////// // NOTE: [$SARR] Dqn_SArray ////////////////////////////////////////////////////////////////////////
template <typename T> Dqn_SArray<T> Dqn_SArray_Init(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) template <typename T> Dqn_SArray<T> Dqn_SArray_Init(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem)
{ {
Dqn_SArray<T> result = {}; Dqn_SArray<T> result = {};
@ -700,7 +705,7 @@ template <typename T> T *Dqn_SArray_Add(Dqn_SArray<T> *array, T const &item)
return result; return result;
} }
template <typename T, Dqn_usize N> T *Dqn_SArray_InsertArray(Dqn_SArray<T> *array, Dqn_usize index, T const *items, Dqn_usize count) template <typename T> T *Dqn_SArray_InsertArray(Dqn_SArray<T> *array, Dqn_usize index, T const *items, Dqn_usize count)
{ {
T *result = nullptr; T *result = nullptr;
if (!Dqn_SArray_IsValid(array)) if (!Dqn_SArray_IsValid(array))
@ -715,7 +720,7 @@ template <typename T, Dqn_usize N> T *Dqn_SArray_InsertCArray(Dqn_SArray<T> *arr
return result; return result;
} }
template <typename T, Dqn_usize N> T *Dqn_SArray_Insert(Dqn_SArray<T> *array, Dqn_usize index, T const &item) template <typename T> T *Dqn_SArray_Insert(Dqn_SArray<T> *array, Dqn_usize index, T const &item)
{ {
T *result = Dqn_SArray_InsertArray(array, index, &item, 1); T *result = Dqn_SArray_InsertArray(array, index, &item, 1);
return result; return result;
@ -896,6 +901,19 @@ Dqn_Slice<T> Dqn_Slice_InitCArray(Dqn_Arena *arena, T const (&array)[N])
return result; return result;
} }
template <typename T> Dqn_Slice<T> Dqn_Slice_CopyPtr(Dqn_Arena *arena, T *const data, Dqn_usize size)
{
T *copy = Dqn_Arena_NewArrayCopy(arena, T, data, size);
Dqn_Slice<T> result = Dqn_Slice_Init(copy, copy ? size : 0);
return result;
}
template <typename T> Dqn_Slice<T> Dqn_Slice_Copy(Dqn_Arena *arena, Dqn_Slice<T> slice)
{
Dqn_Slice<T> result = Dqn_Slice_CopyPtr(arena, slice.data, slice.size);
return result;
}
template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem) template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem)
{ {
Dqn_Slice<T> result = {}; Dqn_Slice<T> result = {};
@ -1442,7 +1460,7 @@ template <typename T> DQN_API T *Dqn_List_At(Dqn_List<T> *list, Dqn_usize index,
if (!list || !list->chunk_size || index >= list->count) if (!list || !list->chunk_size || index >= list->count)
return nullptr; 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 desired_chunk = index / list->chunk_size;
Dqn_usize forward_scan_dist = desired_chunk; Dqn_usize forward_scan_dist = desired_chunk;
Dqn_usize backward_scan_dist = total_chunks - desired_chunk; Dqn_usize backward_scan_dist = total_chunks - desired_chunk;
@ -1452,11 +1470,11 @@ template <typename T> DQN_API T *Dqn_List_At(Dqn_List<T> *list, Dqn_usize index,
Dqn_usize current_chunk = 0; Dqn_usize current_chunk = 0;
Dqn_ListChunk<T> **chunk = nullptr; Dqn_ListChunk<T> **chunk = nullptr;
if (forward_scan_dist <= backward_scan_dist) { 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 { } else {
current_chunk = total_chunks; 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--) {
} }
} }

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -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) #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 /////////////////////////////////////////////////////////////////////////// // NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Bit_UnsetInplace(Dqn_usize *flags, Dqn_usize bitfield) 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; 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 /////////////////////////////////////////////////////////////////////// // NOTE: [$DLIB] Dqn_Library ///////////////////////////////////////////////////////////////////////
Dqn_Library *g_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) if (result->lib_init)
return result; 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 ///////////////////////////////////////////////////////////////////////// // NOTE: Query OS info /////////////////////////////////////////////////////////////////////////
{ {
@ -1070,7 +1019,6 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
#endif #endif
} }
// NOTE Initialise fields ////////////////////////////////////////////////////////////////////// // NOTE Initialise fields //////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
result->profiler = &result->profiler_default_instance; 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); result->exe_dir = Dqn_OS_EXEDir(&result->arena);
// NOTE: Print out init features /////////////////////////////////////////////////////////////// // NOTE: Print out init features ///////////////////////////////////////////////////////////////
if (on_init == Dqn_LibraryOnInit_LogFeatures) { Dqn_Str8Builder builder = {};
Dqn_Str8Builder builder = {}; builder.arena = scratch.arena;
builder.arena = scratch.arena; if (on_init & Dqn_LibraryOnInit_LogLibFeatures) {
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n")); Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
Dqn_f64 page_size_kib = result->os_page_size / 1024.0; 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 #endif
// TODO(doyle): Add stacktrace feature log // 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; return result;
} }
@ -1350,7 +1324,7 @@ DQN_API bool Dqn_OS_JobQueueSPMCAddArray(Dqn_JobQueueSPMC *queue, Dqn_Job *jobs,
return false; return false;
for (size_t offset = 0; offset < count; offset++) { 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]; 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_API void Dqn_OS_JobQueueSPMCWaitForCompletion(Dqn_JobQueueSPMC *queue)
{ {
Dqn_OS_MutexLock(&queue->mutex); Dqn_OS_MutexLock(&queue->mutex);
if (queue->read_index == queue->write_index) { if (queue->finish_index == queue->write_index) {
Dqn_OS_MutexUnlock(&queue->mutex); Dqn_OS_MutexUnlock(&queue->mutex);
return; return;
} }

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -50,21 +53,6 @@ struct Dqn_JSONBuilder
}; };
#endif // !defined(DQN_NO_JSON_BUIDLER) #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 ////////////////////////////////////////////////////////////////// // NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
@ -112,7 +100,7 @@ struct Dqn_BinarySearchResult
// NOTE: [$MISC] Misc ////////////////////////////////////////////////////////////////////////////// // NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
struct Dqn_U64Str8 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; 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, 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) #if !defined(DQN_NO_PROFILER)
// NOTE: [$PROF] Dqn_Profiler ////////////////////////////////////////////////////////////////////// // NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE) #if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
@ -182,10 +182,17 @@ struct Dqn_ProfilerZoneScope
~Dqn_ProfilerZoneScope(); ~Dqn_ProfilerZoneScope();
Dqn_ProfilerZone zone; 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_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 #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 enum Dqn_ProfilerAnchorBuffer
{ {
Dqn_ProfilerAnchorBuffer_Back, Dqn_ProfilerAnchorBuffer_Back,
@ -207,10 +214,13 @@ struct Dqn_Job
bool add_to_completion_queue; bool add_to_completion_queue;
Dqn_Arena *arena; Dqn_Arena *arena;
Dqn_JobQueueFunc *func; Dqn_JobQueueFunc *func;
uint32_t user_tag;
void *user_context; void *user_context;
}; };
#if !defined(DQN_JOB_QUEUE_SPMC_SIZE)
#define DQN_JOB_QUEUE_SPMC_SIZE 128
#endif
struct Dqn_JobQueueSPMC struct Dqn_JobQueueSPMC
{ {
Dqn_OSMutex mutex; Dqn_OSMutex mutex;
@ -218,7 +228,7 @@ struct Dqn_JobQueueSPMC
Dqn_OSSemaphore wait_for_completion_semaphore; Dqn_OSSemaphore wait_for_completion_semaphore;
uint32_t threads_waiting_for_completion; uint32_t threads_waiting_for_completion;
Dqn_Job jobs[64]; Dqn_Job jobs[DQN_JOB_QUEUE_SPMC_SIZE];
Dqn_b32 quit; Dqn_b32 quit;
uint32_t quit_exit_code; uint32_t quit_exit_code;
uint32_t volatile read_index; uint32_t volatile read_index;
@ -226,7 +236,7 @@ struct Dqn_JobQueueSPMC
uint32_t volatile write_index; uint32_t volatile write_index;
Dqn_OSSemaphore complete_queue_write_semaphore; 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_read_index;
uint32_t volatile complete_write_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` bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
Dqn_TicketMutex lib_mutex; Dqn_TicketMutex lib_mutex;
Dqn_TicketMutex thread_context_init_mutex;
Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Str8 exe_dir; // The directory of the current executable
Dqn_Arena arena; Dqn_Arena arena;
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
Dqn_ArenaCatalog arena_catalog; Dqn_ArenaCatalog arena_catalog;
bool slow_verification_checks; // Enable expensive library verification checks bool slow_verification_checks; // Enable expensive library verification checks
Dqn_CPUReport cpu_report;
// NOTE: Logging /////////////////////////////////////////////////////////////////////////////// // NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
void * log_user_data; void * log_user_data;
@ -275,8 +287,10 @@ struct Dqn_Library
enum Dqn_LibraryOnInit enum Dqn_LibraryOnInit
{ {
Dqn_LibraryOnInit_Nil, Dqn_LibraryOnInit_Nil = 0,
Dqn_LibraryOnInit_LogFeatures, Dqn_LibraryOnInit_LogLibFeatures = 1 << 0,
Dqn_LibraryOnInit_LogCPUFeatures = 1 << 1,
Dqn_LibraryOnInit_LogAllFeatures = Dqn_LibraryOnInit_LogLibFeatures | Dqn_LibraryOnInit_LogCPUFeatures,
}; };
// NOTE: [$PCGX] Dqn_PCG32 ///////////////////////////////////////////////////////////////////////// // 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_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value) #define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value)
#endif // !defined(DQN_NO_JSON_BUILDER) #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 ////////////////////////////////////////////////////////////////// // NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T> bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); template <typename T> 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 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_IsSet (Dqn_usize bits, Dqn_usize bits_to_set);
DQN_API bool Dqn_Bit_IsNotSet (Dqn_usize bits, Dqn_usize bits_to_check); 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 ////////////////////////////////////////////////////////////////////////// // NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b); 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_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize 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 ////////////////////////////////////////////////////////////////////// // NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1) #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); DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index);

429
dqn_json.cpp Normal file
View File

@ -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);
}
}

View File

@ -1,9 +1,10 @@
#pragma once
#include "dqn.h"
#if !defined(SHEREDOM_JSON_H_INCLUDED) #if !defined(SHEREDOM_JSON_H_INCLUDED)
#error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file #error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file
#endif #endif
#if !defined(DQN_JSON_H)
#define DQN_JSON_H
// NOTE: Dqn_JSON ////////////////////////////////////////////////////////////////////////////////// // NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count); 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) #define Dqn_JSON_ItErrorUnknownKeyValue(it) Dqn_JSON_ItErrorUnknownKeyValue_(it, DQN_CALL_SITE)
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite 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)

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -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_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena); Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena);
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; 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); result = Dqn_Str8_Copy(arena, split.lhs);
return result; return result;
} }

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -222,17 +225,6 @@ enum Dqn_OSSemaphoreWaitResult
}; };
#endif // !defined(DQN_NO_SEMAPHORE) #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 ///////////////////////////////////////////////////////////////////// // NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE) #if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*); 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_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 Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 (Dqn_OSDateTime time, char date_separator = '-', char hms_separator = ':');
DQN_API uint64_t Dqn_OS_DateUnixTime (); 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 uint64_t Dqn_OS_DateToUnixTime (Dqn_OSDateTime date);
DQN_API bool Dqn_OS_DateIsValid (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) #endif // !defined(DQN_NO_SEMAPHORE)
// NOTE: [$MUTX] Dqn_OSMutex /////////////////////////////////////////////////////////////////////// // 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_MutexDeinit(Dqn_OSMutex *mutex);
DQN_API void Dqn_OS_MutexLock (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_MutexUnlock(Dqn_OSMutex *mutex);
// NOTE: [$THRD] Dqn_OSThread ///////////////////////////////////////////////////////////////////// // NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE) #if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)

View File

@ -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; 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) DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size)
{ {
#if defined(DQN_PLATFORM_EMSCRIPTEN) #if defined(DQN_PLATFORM_EMSCRIPTEN)

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
@ -127,6 +130,30 @@ DQN_API uint64_t Dqn_OS_DateUnixTime()
return result; 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_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date)
{ {
DQN_ASSERT(Dqn_OS_DateIsValid(date)); DQN_ASSERT(Dqn_OS_DateIsValid(date));

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\

View File

@ -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; 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 = {}; Dqn_Str8BinarySplitResult result = {};
if (!Dqn_Str8_HasData(string) || !find || find_size == 0) 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; 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; return result;
} }
@ -218,7 +221,7 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin
return result; 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 = {}; Dqn_Str8FindResult result = {};
if (!Dqn_Str8_HasData(string) || !find || find_size == 0) 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; 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; 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 = {}; Dqn_Str8FindResult result = {};
for (size_t index = 0; !result.found && index < string.size; index++) { 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_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path)
{ {
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")}; 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; Dqn_Str8 result = Dqn_Str8_HasData(split.rhs) ? split.rhs : split.lhs;
return result; 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_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; Dqn_Str8 result = split.lhs;
return result; return result;
} }
DQN_API Dqn_Str8 Dqn_Str8_FileExtension(Dqn_Str8 path) 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; Dqn_Str8 result = split.rhs;
return result; return result;
} }
@ -895,17 +898,18 @@ DQN_API bool Dqn_Char_IsHex(char ch)
return result; 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); Dqn_CharHexToU8 result = {};
result.success = true;
uint8_t result = 0;
if (ch >= 'a' && ch <= 'f') if (ch >= 'a' && ch <= 'f')
result = ch - 'a' + 10; result.value = ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F') 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 else
result = ch - '0'; result.success = false;
return result; return result;
} }

View File

@ -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_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_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_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_BinarySplitLastArray (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_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_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> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode); DQN_API Dqn_Slice<Dqn_Str8> 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_FindStr8Array (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_FindStr8 (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_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_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); 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 <Dqn_usize N> bool operator!=
#endif // !defined(DQN_NO_FSTR8) #endif // !defined(DQN_NO_FSTR8)
// NOTE: [$CHAR] Dqn_Char ////////////////////////////////////////////////////////////////////////// // 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_IsAlphabet (char ch);
DQN_API bool Dqn_Char_IsDigit (char ch); DQN_API bool Dqn_Char_IsDigit (char ch);
DQN_API bool Dqn_Char_IsAlphaNum (char ch); DQN_API bool Dqn_Char_IsAlphaNum (char ch);
DQN_API bool Dqn_Char_IsWhitespace (char ch); DQN_API bool Dqn_Char_IsWhitespace (char ch);
DQN_API bool Dqn_Char_IsHex (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_ToHex (char ch);
DQN_API char Dqn_Char_ToHexUnchecked (char ch); DQN_API char Dqn_Char_ToHexUnchecked (char ch);
DQN_API char Dqn_Char_ToLower (char ch); DQN_API char Dqn_Char_ToLower (char ch);

View File

@ -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()"); DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
// NOTE: Setup scratch arenas ////////////////////////////////////////////////////////////////// // NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex);
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) { DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
// NOTE: We allocate arenas so that they all come from the memory // 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; result->error_sink.arena = result->error_sink_arena;
} }
Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex);
return result; return result;
} }

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/* /*
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -30,6 +33,7 @@ struct Dqn_TypeField
Dqn_isize value; Dqn_isize value;
Dqn_usize offset_of; Dqn_usize offset_of;
Dqn_usize size_of; Dqn_usize size_of;
Dqn_usize align_of;
Dqn_Str8 type_decl; Dqn_Str8 type_decl;
uint32_t type_enum; uint32_t type_enum;
bool is_pointer; bool is_pointer;

View File

@ -3,6 +3,333 @@
#include <inttypes.h> #include <inttypes.h>
#if !defined(__clang__)
// NOTE: Taken from MSDN __cpuid example implementation
// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170
#include <bitset>
#include <string>
#include <vector>
#include <array>
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<int, 4> 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<int*>(vendor) = data_[0][1];
*reinterpret_cast<int*>(vendor + 4) = data_[0][3];
*reinterpret_cast<int*>(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<std::array<int, 4>> data_;
std::vector<std::array<int, 4>> 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() static Dqn_UTest Dqn_Test_Arena()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
@ -104,79 +431,79 @@ static Dqn_UTest Dqn_Test_Bin()
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Bin") { DQN_UTEST_GROUP(test, "Dqn_Bin") {
DQN_UTEST_TEST("Convert 0x123") { 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_ASSERTF(&test, result == 0x123, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert 0xFFFF") { 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_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert FFFF") { 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_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert abCD") { 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_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert 0xabCD") { 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_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert 0x") { 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_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert 0X") { 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_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert 3") { 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_ASSERTF(&test, result == 3, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert f") { 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_ASSERTF(&test, result == 0xf, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert g") { 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_ASSERTF(&test, result == 0, "result: %" PRIu64, result);
} }
DQN_UTEST_TEST("Convert -0x3") { 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); DQN_UTEST_ASSERTF(&test, result == 0, "result: %" PRIu64, result);
} }
uint32_t number = 0xd095f6; uint32_t number = 0xd095f6;
DQN_UTEST_TEST("Convert %x to string", number) { 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)); DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
} }
number = 0xf6ed00; number = 0xf6ed00;
DQN_UTEST_TEST("Convert %x to string", number) { 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_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_Str8 hex = DQN_STR8("0xf6ed00");
DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) { 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_UTEST_ASSERTF(&test,
Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")), Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")),
"number_hex=%.*s", "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) void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
{ {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); 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) switch(hash_type)
{ {
@ -1619,12 +1946,11 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %" PRIu64, result.value); 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_UTEST_TEST("Find: String (char) is not in buffer") {
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
Dqn_Str8 find = DQN_STR8("2"); 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.found);
DQN_UTEST_ASSERT(&test, result.index == 0); DQN_UTEST_ASSERT(&test, result.index == 0);
DQN_UTEST_ASSERT(&test, result.match.data == nullptr); 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_UTEST_TEST("Find: String (char) is in buffer") {
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"); Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
Dqn_Str8 find = DQN_STR8("6"); 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.found);
DQN_UTEST_ASSERT(&test, result.index == 2); DQN_UTEST_ASSERT(&test, result.index == 2);
DQN_UTEST_ASSERT(&test, result.match.data[0] == '6'); 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_UTEST_TEST("File name from Windows path") {
Dqn_Str8 buf = DQN_STR8("C:\\ABC\\test.exe"); Dqn_Str8 buf = DQN_STR8("C:\\ABC\\test.exe");
Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf); Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf);
@ -1897,6 +2222,7 @@ void Dqn_Test_RunSuite()
{ {
Dqn_UTest tests[] = Dqn_UTest tests[] =
{ {
Dqn_Test_Base(),
Dqn_Test_Arena(), Dqn_Test_Arena(),
Dqn_Test_Bin(), Dqn_Test_Bin(),
Dqn_Test_BinarySearch(), Dqn_Test_BinarySearch(),
@ -1933,7 +2259,7 @@ void Dqn_Test_RunSuite()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
(void)argv; (void)argc; (void)argv; (void)argc;
Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures); Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures);
Dqn_Test_RunSuite(); Dqn_Test_RunSuite();
return 0; return 0;
} }

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ // $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\

View File

@ -9,21 +9,8 @@ separated by category into files for organisation. You only need to include
## Build ## Build
To build with this library, copy all the `*.[h|cpp]` files at the root of the 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 repository to your desired location and compile `dqn.cpp` or include it into one
header file include the header. of your translation units.
```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"
```
Finally ensure that the compiler has in its search paths for the include Finally ensure that the compiler has in its search paths for the include
directory where headers are located, e.g. `-I <path/to/dqn/headers>`. directory where headers are located, e.g. `-I <path/to/dqn/headers>`.