Pull in bug fixes from upstream
This commit is contained in:
parent
243a214b43
commit
765b8255f7
@ -13,7 +13,7 @@ pushd Build
|
||||
REM O2 Optimisation Level 2
|
||||
REM Oi Use CPU Intrinsics
|
||||
REM Z7 Combine multi-debug files to one debug file
|
||||
set common_flags=-D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h
|
||||
set common_flags=-D DQN_WITH_UNIT_TESTS -D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF %script_dir%\dqn.cpp
|
||||
|
||||
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo
|
||||
|
||||
|
3
build.sh
3
build.sh
@ -11,10 +11,9 @@ pushd Build
|
||||
-Werror \
|
||||
-fsanitize=address \
|
||||
-std=c++17 \
|
||||
-D DQN_IMPLEMENTATION \
|
||||
-D DQN_UNIT_TESTS_WITH_MAIN \
|
||||
-D DQN_UNIT_TESTS_WITH_KECCAK \
|
||||
-x c++ ${code_dir}/dqn.h \
|
||||
-x ${code_dir}/dqn.cpp \
|
||||
-g \
|
||||
-o dqn_unit_tests
|
||||
popd
|
||||
|
62
dqn.cpp
Normal file
62
dqn.cpp
Normal 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
79
dqn.h
@ -1,4 +1,4 @@
|
||||
#if !defined(DQN_H)
|
||||
#pragma once
|
||||
#define DQN_H
|
||||
|
||||
/*
|
||||
@ -15,6 +15,7 @@
|
||||
// \___|
|
||||
//
|
||||
// dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dqn
|
||||
// ASCII -- BigMoney-NW by Nathan Bloomfild
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -42,11 +43,7 @@
|
||||
//
|
||||
// -- Compiling --
|
||||
//
|
||||
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
|
||||
// enable the implementation of this library, for example:
|
||||
//
|
||||
// #define DQN_IMEPLEMENTATION
|
||||
// #include "dqn.h"
|
||||
// Compile dqn.cpp or include it into one of your translation units.
|
||||
//
|
||||
// Additionally, this library supports including/excluding specific sections
|
||||
// of the library by using #define on the name of the section. These names are
|
||||
@ -208,6 +205,14 @@
|
||||
// library from being included. This might be useful if you are including the
|
||||
// library in your project yourself. The library must still be defined and
|
||||
// visible before this header.
|
||||
//
|
||||
// - Enable compilation of unit tests with the library.
|
||||
//
|
||||
// #define DQN_WITH_UNIT_TESTS
|
||||
//
|
||||
// - Increase the capacity of the job queue, default is 128.
|
||||
//
|
||||
// #define DQN_JOB_QUEUE_SPMC_SIZE 128
|
||||
*/
|
||||
|
||||
#if defined(DQN_ONLY_VARRAY) || \
|
||||
@ -343,65 +348,3 @@
|
||||
#endif
|
||||
#include "dqn_json.h"
|
||||
#endif
|
||||
#endif // DQN_H
|
||||
|
||||
#if defined(DQN_IMPLEMENTATION)
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
|
||||
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
|
||||
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
|
||||
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
|
||||
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
|
||||
// $$ | $$ |\$ /$$ |$$ | $$ |
|
||||
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
|
||||
// \______|\__| \__|\__| \________|
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if defined(DQN_WITH_CGEN)
|
||||
#if !defined(DQN_NO_METADESK)
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<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
|
||||
|
@ -345,7 +345,8 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
|
||||
|
||||
// NOTE: Store the offset to the original pointer behind the user's
|
||||
// pointer.
|
||||
DQN_MEMCPY(&(DQN_CAST(char *)slot->data)[-1], &offset_to_original_ptr, 1);
|
||||
char *offset_to_original_storage = DQN_CAST(char *)slot->data - 1;
|
||||
DQN_MEMCPY(offset_to_original_storage, &offset_to_original_ptr, 1);
|
||||
}
|
||||
|
||||
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
|
||||
@ -427,6 +428,7 @@ DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *poo
|
||||
|
||||
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_ArenaCatalogItem *result = &catalog->sentinel;
|
||||
for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
|
||||
if (item->label == label) {
|
||||
@ -434,6 +436,7 @@ DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, D
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -462,7 +465,9 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy);
|
||||
}
|
||||
|
||||
@ -470,14 +475,18 @@ DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena,
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
va_end(args);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
}
|
||||
|
||||
@ -494,14 +503,18 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelRef(Dqn_ArenaCatalog *catalog, Dqn
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
return result;
|
||||
}
|
||||
@ -510,7 +523,9 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
va_end(args);
|
||||
return result;
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -51,6 +54,7 @@ struct Dqn_Arena
|
||||
{
|
||||
Dqn_ArenaBlock *curr;
|
||||
uint8_t flags;
|
||||
Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use.
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMem
|
||||
|
286
dqn_avx512f.cpp
Normal file
286
dqn_avx512f.cpp
Normal 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
30
dqn_avx512f.h
Normal 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
|
201
dqn_base.cpp
201
dqn_base.cpp
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -21,16 +24,196 @@
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
DQN_API Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||
Dqn_CPUFeatureDecl g_dqn_cpu_feature_decl[Dqn_CPUFeature_Count];
|
||||
|
||||
DQN_API Dqn_CPUIDResult Dqn_CPU_ID(Dqn_CPUIDArgs args)
|
||||
{
|
||||
Dqn_CPUIDRegisters result = {};
|
||||
#if defined(DQN_COMPILER_MSVC)
|
||||
__cpuid(DQN_CAST(int *)result.array, function_id);
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
__get_cpuid(function_id, &result.array[0] /*eax*/, &result.array[1] /*ebx*/, &result.array[2] /*ecx*/ , &result.array[3] /*edx*/);
|
||||
#else
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
Dqn_CPUIDResult result = {};
|
||||
__cpuidex(result.values, args.eax, args.ecx);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_CPU_HasFeatureArray(Dqn_CPUReport const *report, Dqn_CPUFeatureQuery *features, Dqn_usize features_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
Dqn_usize const BITS = sizeof(report->features[0]) * 8;
|
||||
DQN_FOR_UINDEX(feature_index, features_size) {
|
||||
Dqn_CPUFeatureQuery *query = features + feature_index;
|
||||
Dqn_usize chunk_index = query->feature / BITS;
|
||||
Dqn_usize chunk_bit = query->feature % BITS;
|
||||
uint64_t chunk = report->features[chunk_index];
|
||||
query->available = chunk & (1ULL << chunk_bit);
|
||||
result += DQN_CAST(int)query->available;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_CPU_HasFeature(Dqn_CPUReport const *report, Dqn_CPUFeature feature)
|
||||
{
|
||||
Dqn_CPUFeatureQuery query = {};
|
||||
query.feature = feature;
|
||||
bool result = Dqn_CPU_HasFeatureArray(report, &query, 1) == 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_CPU_HasAllFeatures(Dqn_CPUReport const *report, Dqn_CPUFeature const *features, Dqn_usize features_size)
|
||||
{
|
||||
bool result = true;
|
||||
for (Dqn_usize index = 0; result && index < features_size; index++)
|
||||
result &= Dqn_CPU_HasFeature(report, features[index]);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_CPU_SetFeature(Dqn_CPUReport *report, Dqn_CPUFeature feature)
|
||||
{
|
||||
DQN_ASSERT(feature < Dqn_CPUFeature_Count);
|
||||
Dqn_usize const BITS = sizeof(report->features[0]) * 8;
|
||||
Dqn_usize chunk_index = feature / BITS;
|
||||
Dqn_usize chunk_bit = feature % BITS;
|
||||
report->features[chunk_index] |= (1ULL << chunk_bit);
|
||||
}
|
||||
|
||||
DQN_API Dqn_CPUReport Dqn_CPU_Report()
|
||||
{
|
||||
Dqn_CPUReport result = {};
|
||||
Dqn_CPUIDResult fn_0000_[16] = {};
|
||||
Dqn_CPUIDResult fn_8000_[64] = {};
|
||||
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
|
||||
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
|
||||
|
||||
// NOTE: Query standard/extended numbers ///////////////////////////////////////////////////////
|
||||
{
|
||||
Dqn_CPUIDArgs args = {};
|
||||
|
||||
// NOTE: Query standard function (e.g. eax = 0x0) for function count + cpu vendor
|
||||
args = {};
|
||||
fn_0000_[0] = Dqn_CPU_ID(args);
|
||||
|
||||
// NOTE: Query extended function (e.g. eax = 0x8000'0000) for function count + cpu vendor
|
||||
args = {};
|
||||
args.eax = DQN_CAST(int) EXTENDED_FUNC_BASE_EAX;
|
||||
fn_8000_[0] = Dqn_CPU_ID(args);
|
||||
}
|
||||
|
||||
// NOTE: Extract function count ////////////////////////////////////////////////////////////////
|
||||
int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax;
|
||||
int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax;
|
||||
|
||||
// NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
|
||||
{
|
||||
DQN_ASSERT((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_));
|
||||
DQN_ASSERT((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_));
|
||||
|
||||
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
|
||||
Dqn_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_0000_[eax] = Dqn_CPU_ID(args);
|
||||
}
|
||||
|
||||
for (int eax = EXTENDED_FUNC_BASE_EAX + 1, index = 1; eax <= EXTENDED_FUNC_MAX_EAX; eax++, index++) {
|
||||
Dqn_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_8000_[index] = Dqn_CPU_ID(args);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Query CPU vendor //////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DQN_MEMCPY(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU brand ///////////////////////////////////////////////////////////////////////
|
||||
if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) {
|
||||
DQN_MEMCPY(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 8, &fn_8000_[0x0002].reg.ecx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 12, &fn_8000_[0x0002].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DQN_MEMCPY(result.brand + 16, &fn_8000_[0x0003].reg.eax, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 20, &fn_8000_[0x0003].reg.ebx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 24, &fn_8000_[0x0003].reg.ecx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 28, &fn_8000_[0x0003].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DQN_MEMCPY(result.brand + 32, &fn_8000_[0x0004].reg.eax, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 36, &fn_8000_[0x0004].reg.ebx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 40, &fn_8000_[0x0004].reg.ecx, REGISTER_SIZE);
|
||||
DQN_MEMCPY(result.brand + 44, &fn_8000_[0x0004].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DQN_ASSERT(result.brand[sizeof(result.brand) - 1] == 0);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU features //////////////////////////////////////////////////////////////////
|
||||
for (Dqn_usize ext_index = 0; ext_index < Dqn_CPUFeature_Count; ext_index++) {
|
||||
bool available = false;
|
||||
|
||||
// NOTE: Mask bits taken from various manuals
|
||||
// - AMD64 Architecture Programmer's Manual, Volumes 1-5
|
||||
// - https://en.wikipedia.org/wiki/CPUID#Calling_CPUID
|
||||
switch (DQN_CAST(Dqn_CPUFeature)ext_index) {
|
||||
case Dqn_CPUFeature_3DNow: available = (fn_8000_[0x0001].reg.edx & (1 << 31)); break;
|
||||
case Dqn_CPUFeature_3DNowExt: available = (fn_8000_[0x0001].reg.edx & (1 << 30)); break;
|
||||
case Dqn_CPUFeature_ABM: available = (fn_8000_[0x0001].reg.ecx & (1 << 5)); break;
|
||||
case Dqn_CPUFeature_AES: available = (fn_0000_[0x0001].reg.ecx & (1 << 25)); break;
|
||||
case Dqn_CPUFeature_AVX: available = (fn_0000_[0x0001].reg.ecx & (1 << 28)); break;
|
||||
case Dqn_CPUFeature_AVX2: available = (fn_0000_[0x0007].reg.ebx & (1 << 0)); break;
|
||||
case Dqn_CPUFeature_AVX512F: available = (fn_0000_[0x0007].reg.ebx & (1 << 16)); break;
|
||||
case Dqn_CPUFeature_AVX512DQ: available = (fn_0000_[0x0007].reg.ebx & (1 << 17)); break;
|
||||
case Dqn_CPUFeature_AVX512IFMA: available = (fn_0000_[0x0007].reg.ebx & (1 << 21)); break;
|
||||
case Dqn_CPUFeature_AVX512PF: available = (fn_0000_[0x0007].reg.ebx & (1 << 26)); break;
|
||||
case Dqn_CPUFeature_AVX512ER: available = (fn_0000_[0x0007].reg.ebx & (1 << 27)); break;
|
||||
case Dqn_CPUFeature_AVX512CD: available = (fn_0000_[0x0007].reg.ebx & (1 << 28)); break;
|
||||
case Dqn_CPUFeature_AVX512BW: available = (fn_0000_[0x0007].reg.ebx & (1 << 30)); break;
|
||||
case Dqn_CPUFeature_AVX512VL: available = (fn_0000_[0x0007].reg.ebx & (1 << 31)); break;
|
||||
case Dqn_CPUFeature_AVX512VBMI: available = (fn_0000_[0x0007].reg.ecx & (1 << 1)); break;
|
||||
case Dqn_CPUFeature_AVX512VBMI2: available = (fn_0000_[0x0007].reg.ecx & (1 << 6)); break;
|
||||
case Dqn_CPUFeature_AVX512VNNI: available = (fn_0000_[0x0007].reg.ecx & (1 << 11)); break;
|
||||
case Dqn_CPUFeature_AVX512BITALG: available = (fn_0000_[0x0007].reg.ecx & (1 << 12)); break;
|
||||
case Dqn_CPUFeature_AVX512VPOPCNTDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 14)); break;
|
||||
case Dqn_CPUFeature_AVX5124VNNIW: available = (fn_0000_[0x0007].reg.edx & (1 << 2)); break;
|
||||
case Dqn_CPUFeature_AVX5124FMAPS: available = (fn_0000_[0x0007].reg.edx & (1 << 3)); break;
|
||||
case Dqn_CPUFeature_AVX512VP2INTERSECT: available = (fn_0000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case Dqn_CPUFeature_AVX512FP16: available = (fn_0000_[0x0007].reg.edx & (1 << 23)); break;
|
||||
case Dqn_CPUFeature_CLZERO: available = (fn_8000_[0x0008].reg.ebx & (1 << 0)); break;
|
||||
case Dqn_CPUFeature_CMPXCHG8B: available = (fn_0000_[0x0001].reg.edx & (1 << 8)); break;
|
||||
case Dqn_CPUFeature_CMPXCHG16B: available = (fn_0000_[0x0001].reg.ecx & (1 << 13)); break;
|
||||
case Dqn_CPUFeature_F16C: available = (fn_0000_[0x0001].reg.ecx & (1 << 29)); break;
|
||||
case Dqn_CPUFeature_FMA: available = (fn_0000_[0x0001].reg.ecx & (1 << 12)); break;
|
||||
case Dqn_CPUFeature_FMA4: available = (fn_8000_[0x0001].reg.ecx & (1 << 16)); break;
|
||||
case Dqn_CPUFeature_FP128: available = (fn_8000_[0x001A].reg.eax & (1 << 0)); break;
|
||||
case Dqn_CPUFeature_FP256: available = (fn_8000_[0x001A].reg.eax & (1 << 2)); break;
|
||||
case Dqn_CPUFeature_FPU: available = (fn_0000_[0x0001].reg.edx & (1 << 0)); break;
|
||||
case Dqn_CPUFeature_MMX: available = (fn_0000_[0x0001].reg.edx & (1 << 23)); break;
|
||||
case Dqn_CPUFeature_MONITOR: available = (fn_0000_[0x0001].reg.ecx & (1 << 3)); break;
|
||||
case Dqn_CPUFeature_MOVBE: available = (fn_0000_[0x0001].reg.ecx & (1 << 22)); break;
|
||||
case Dqn_CPUFeature_MOVU: available = (fn_8000_[0x001A].reg.eax & (1 << 1)); break;
|
||||
case Dqn_CPUFeature_MmxExt: available = (fn_8000_[0x0001].reg.edx & (1 << 22)); break;
|
||||
case Dqn_CPUFeature_PCLMULQDQ: available = (fn_0000_[0x0001].reg.ecx & (1 << 1)); break;
|
||||
case Dqn_CPUFeature_POPCNT: available = (fn_0000_[0x0001].reg.ecx & (1 << 23)); break;
|
||||
case Dqn_CPUFeature_RDRAND: available = (fn_0000_[0x0001].reg.ecx & (1 << 30)); break;
|
||||
case Dqn_CPUFeature_RDSEED: available = (fn_0000_[0x0007].reg.ebx & (1 << 18)); break;
|
||||
case Dqn_CPUFeature_RDTSCP: available = (fn_8000_[0x0001].reg.edx & (1 << 27)); break;
|
||||
case Dqn_CPUFeature_SHA: available = (fn_0000_[0x0007].reg.ebx & (1 << 29)); break;
|
||||
case Dqn_CPUFeature_SSE: available = (fn_0000_[0x0001].reg.edx & (1 << 25)); break;
|
||||
case Dqn_CPUFeature_SSE2: available = (fn_0000_[0x0001].reg.edx & (1 << 26)); break;
|
||||
case Dqn_CPUFeature_SSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 0)); break;
|
||||
case Dqn_CPUFeature_SSE41: available = (fn_0000_[0x0001].reg.ecx & (1 << 19)); break;
|
||||
case Dqn_CPUFeature_SSE42: available = (fn_0000_[0x0001].reg.ecx & (1 << 20)); break;
|
||||
case Dqn_CPUFeature_SSE4A: available = (fn_8000_[0x0001].reg.ecx & (1 << 6)); break;
|
||||
case Dqn_CPUFeature_SSSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 9)); break;
|
||||
case Dqn_CPUFeature_TSC: available = (fn_0000_[0x0001].reg.edx & (1 << 4)); break;
|
||||
case Dqn_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case Dqn_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break;
|
||||
case Dqn_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break;
|
||||
case Dqn_CPUFeature_Count: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
|
||||
if (available)
|
||||
Dqn_CPU_SetFeature(&result, DQN_CAST(Dqn_CPUFeature)ext_index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
|
130
dqn_base.h
130
dqn_base.h
@ -73,6 +73,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h> // PRIu64...
|
||||
|
||||
#if !defined(DQN_OS_WIN32)
|
||||
#include <stdlib.h> // exit()
|
||||
@ -455,10 +456,110 @@ struct Dqn_ErrorSink
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_PLATFORM_ARM64)
|
||||
struct Dqn_CPUIDRegisters
|
||||
struct Dqn_CPURegisters
|
||||
{
|
||||
Dqn_uint array[4]; // Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
|
||||
int eax;
|
||||
int ebx;
|
||||
int ecx;
|
||||
int edx;
|
||||
};
|
||||
|
||||
union Dqn_CPUIDResult
|
||||
{
|
||||
Dqn_CPURegisters reg;
|
||||
int values[4];
|
||||
};
|
||||
|
||||
struct Dqn_CPUIDArgs
|
||||
{
|
||||
int eax;
|
||||
int ecx;
|
||||
};
|
||||
|
||||
#define DQN_CPU_FEAT_XMACRO \
|
||||
DQN_CPU_FEAT_XENTRY(3DNow) \
|
||||
DQN_CPU_FEAT_XENTRY(3DNowExt) \
|
||||
DQN_CPU_FEAT_XENTRY(ABM) \
|
||||
DQN_CPU_FEAT_XENTRY(AES) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX2) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512F) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512DQ) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512IFMA) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512PF) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512ER) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512CD) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512BW) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VL) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VBMI) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VBMI2) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VNNI) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512BITALG) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VPOPCNTDQ) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX5124VNNIW) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX5124FMAPS) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512VP2INTERSECT) \
|
||||
DQN_CPU_FEAT_XENTRY(AVX512FP16) \
|
||||
DQN_CPU_FEAT_XENTRY(CLZERO) \
|
||||
DQN_CPU_FEAT_XENTRY(CMPXCHG8B) \
|
||||
DQN_CPU_FEAT_XENTRY(CMPXCHG16B) \
|
||||
DQN_CPU_FEAT_XENTRY(F16C) \
|
||||
DQN_CPU_FEAT_XENTRY(FMA) \
|
||||
DQN_CPU_FEAT_XENTRY(FMA4) \
|
||||
DQN_CPU_FEAT_XENTRY(FP128) \
|
||||
DQN_CPU_FEAT_XENTRY(FP256) \
|
||||
DQN_CPU_FEAT_XENTRY(FPU) \
|
||||
DQN_CPU_FEAT_XENTRY(MMX) \
|
||||
DQN_CPU_FEAT_XENTRY(MONITOR) \
|
||||
DQN_CPU_FEAT_XENTRY(MOVBE) \
|
||||
DQN_CPU_FEAT_XENTRY(MOVU) \
|
||||
DQN_CPU_FEAT_XENTRY(MmxExt) \
|
||||
DQN_CPU_FEAT_XENTRY(PCLMULQDQ) \
|
||||
DQN_CPU_FEAT_XENTRY(POPCNT) \
|
||||
DQN_CPU_FEAT_XENTRY(RDRAND) \
|
||||
DQN_CPU_FEAT_XENTRY(RDSEED) \
|
||||
DQN_CPU_FEAT_XENTRY(RDTSCP) \
|
||||
DQN_CPU_FEAT_XENTRY(SHA) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE2) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE3) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE41) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE42) \
|
||||
DQN_CPU_FEAT_XENTRY(SSE4A) \
|
||||
DQN_CPU_FEAT_XENTRY(SSSE3) \
|
||||
DQN_CPU_FEAT_XENTRY(TSC) \
|
||||
DQN_CPU_FEAT_XENTRY(TscInvariant) \
|
||||
DQN_CPU_FEAT_XENTRY(VAES) \
|
||||
DQN_CPU_FEAT_XENTRY(VPCMULQDQ)
|
||||
|
||||
enum Dqn_CPUFeature
|
||||
{
|
||||
#define DQN_CPU_FEAT_XENTRY(label) Dqn_CPUFeature_##label,
|
||||
DQN_CPU_FEAT_XMACRO
|
||||
#undef DQN_CPU_FEAT_XENTRY
|
||||
Dqn_CPUFeature_Count,
|
||||
};
|
||||
|
||||
struct Dqn_CPUFeatureDecl
|
||||
{
|
||||
Dqn_CPUFeature value;
|
||||
Dqn_Str8 label;
|
||||
};
|
||||
|
||||
struct Dqn_CPUFeatureQuery
|
||||
{
|
||||
Dqn_CPUFeature feature;
|
||||
bool available;
|
||||
};
|
||||
|
||||
struct Dqn_CPUReport
|
||||
{
|
||||
char vendor [4 /*bytes*/ * 3 /*EDX, ECX, EBX*/ + 1 /*null*/];
|
||||
char brand [48];
|
||||
uint64_t features[(Dqn_CPUFeature_Count / (sizeof(uint64_t) * 8)) + 1];
|
||||
};
|
||||
|
||||
extern Dqn_CPUFeatureDecl g_dqn_cpu_feature_decl[Dqn_CPUFeature_Count];
|
||||
#endif // DQN_PLATFORM_ARM64
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
@ -468,6 +569,17 @@ struct Dqn_TicketMutex
|
||||
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
|
||||
};
|
||||
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSMutex
|
||||
{
|
||||
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
char win32_handle[48];
|
||||
#else
|
||||
pthread_mutex_t posix_handle;
|
||||
pthread_mutexattr_t posix_attribs;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_PrintStd
|
||||
{
|
||||
@ -516,7 +628,13 @@ typedef void Dqn_LogProc(Dqn_Str8 type,
|
||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value);
|
||||
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value);
|
||||
#if !defined(DQN_PLATFORM_ARM64)
|
||||
DQN_API Dqn_CPUIDRegisters Dqn_CPUID (int function_id);
|
||||
DQN_API Dqn_CPUIDResult Dqn_CPU_ID (Dqn_CPUIDArgs args);
|
||||
DQN_API Dqn_usize Dqn_CPU_HasFeatureArray (Dqn_CPUReport const *report, Dqn_CPUFeatureQuery *features, Dqn_usize features_size);
|
||||
DQN_API bool Dqn_CPU_HasFeature (Dqn_CPUReport const *report, Dqn_CPUFeature feature);
|
||||
DQN_API bool Dqn_CPU_HasAllFeatures (Dqn_CPUReport const *report, Dqn_CPUFeature const *features, Dqn_usize features_size);
|
||||
template <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
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
@ -671,3 +789,9 @@ DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
template <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;
|
||||
}
|
||||
|
138
dqn_cgen.cpp
138
dqn_cgen.cpp
@ -45,6 +45,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] =
|
||||
Dqn_CGenTableHeaderType_CppType,
|
||||
Dqn_CGenTableHeaderType_CppName,
|
||||
Dqn_CGenTableHeaderType_CppIsPtr,
|
||||
Dqn_CGenTableHeaderType_CppOpEquals,
|
||||
Dqn_CGenTableHeaderType_CppArraySize,
|
||||
Dqn_CGenTableHeaderType_CppArraySizeField,
|
||||
Dqn_CGenTableHeaderType_GenTypeInfo,
|
||||
@ -320,6 +321,7 @@ DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8(Dqn_CGenTableHeaderType type
|
||||
case Dqn_CGenTableHeaderType_CppName: result = DQN_STR8("cpp_name"); break;
|
||||
case Dqn_CGenTableHeaderType_CppValue: result = DQN_STR8("cpp_value"); break;
|
||||
case Dqn_CGenTableHeaderType_CppIsPtr: result = DQN_STR8("cpp_is_ptr"); break;
|
||||
case Dqn_CGenTableHeaderType_CppOpEquals: result = DQN_STR8("cpp_op_equals"); break;
|
||||
case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break;
|
||||
case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break;
|
||||
case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break;
|
||||
@ -626,6 +628,46 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Str8 to enum conversion ////////////////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenEnum)
|
||||
continue;
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "bool success;");
|
||||
Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenEnum)
|
||||
continue;
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Operator == and != ////////////////////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenStruct)
|
||||
continue;
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 cpp_op_equals = it.cgen_table_column[Dqn_CGenTableHeaderType_CppOpEquals].string;
|
||||
if (cpp_op_equals != DQN_STR8("true"))
|
||||
continue;
|
||||
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppLine(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emit & Dqn_CGenEmit_Implementation) {
|
||||
@ -685,7 +727,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size);
|
||||
|
||||
Dqn_CppLine(cpp,
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},",
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*salignof(%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},",
|
||||
row_index,
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
@ -700,6 +742,10 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
|
||||
// NOTE: alignof(a->b)
|
||||
DQN_STR_FMT(cpp_type.column.string),
|
||||
cpp_type_padding, "",
|
||||
|
||||
// NOTE: Type string
|
||||
DQN_STR_FMT(cpp_type.column.string),
|
||||
cpp_type_padding, "",
|
||||
@ -726,7 +772,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
|
||||
Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index);
|
||||
Dqn_CppLine(cpp,
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},",
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), alignof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},",
|
||||
row_index,
|
||||
|
||||
// NOTE: Name string
|
||||
@ -739,6 +785,9 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
// NOTE: sizeof(a)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
|
||||
// NOTE: alignof(a)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
|
||||
// NOTE: ..._Type_...
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name));
|
||||
@ -812,17 +861,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Str8 to enum conversion ////////////////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenEnum)
|
||||
continue;
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "bool success;");
|
||||
Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name));
|
||||
}
|
||||
|
||||
Dqn_CppFuncBlock(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(type_name));
|
||||
Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s_type_fields); index++", DQN_STR_FMT(type_name)) {
|
||||
@ -838,5 +883,84 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Operator == and != ////////////////////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenStruct)
|
||||
continue;
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 cpp_op_equals = it.cgen_table_column[Dqn_CGenTableHeaderType_CppOpEquals].string;
|
||||
if (cpp_op_equals != DQN_STR8("true"))
|
||||
continue;
|
||||
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppFuncBlock(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row);
|
||||
|
||||
// TODO(doyle): Check if we're an integral type or not to double check if we
|
||||
// can use memcmp or operator==
|
||||
if (Dqn_Str8_HasData(cpp_array_size_field.column.string)) {
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppIfOrElseIfBlock(cpp,
|
||||
"lhs.%.*s != rhs.%.*s",
|
||||
DQN_STR_FMT(cpp_array_size_field.column.string),
|
||||
DQN_STR_FMT(cpp_array_size_field.column.string)) {
|
||||
Dqn_CppLine(cpp, "return false;");
|
||||
}
|
||||
}
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppIfOrElseIfBlock(cpp,
|
||||
"DQN_MEMCMP(lhs.%.*s, rhs.%.*s, lhs.%.*s) != 0",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_array_size_field.column.string)) {
|
||||
Dqn_CppLine(cpp, "return false;");
|
||||
}
|
||||
}
|
||||
} else if (Dqn_Str8_HasData(cpp_array_size.column.string)) {
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppIfOrElseIfBlock(cpp,
|
||||
"DQN_MEMCMP(lhs.%.*s, rhs.%.*s, %.*s) != 0",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_array_size.column.string)) {
|
||||
Dqn_CppLine(cpp, "return false;");
|
||||
}
|
||||
}
|
||||
} else if (cpp_is_ptr.column.string == DQN_STR8("true")) {
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppIfOrElseIfBlock(cpp,
|
||||
"*lhs.%.*s != *rhs.%.*s",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_name.column.string)) {
|
||||
Dqn_CppLine(cpp, "return false;");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppIfOrElseIfBlock(cpp,
|
||||
"lhs.%.*s != rhs.%.*s",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_name.column.string)) {
|
||||
Dqn_CppLine(cpp, "return false;");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Dqn_CppLine(cpp, "return true;");
|
||||
}
|
||||
|
||||
Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "bool result = !(lhs == rhs);");
|
||||
Dqn_CppLine(cpp, "return result;");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ enum Dqn_CGenTableHeaderType
|
||||
Dqn_CGenTableHeaderType_CppName,
|
||||
Dqn_CGenTableHeaderType_CppValue,
|
||||
Dqn_CGenTableHeaderType_CppIsPtr,
|
||||
Dqn_CGenTableHeaderType_CppOpEquals,
|
||||
Dqn_CGenTableHeaderType_CppArraySize,
|
||||
Dqn_CGenTableHeaderType_CppArraySizeField,
|
||||
Dqn_CGenTableHeaderType_GenTypeInfo,
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -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)
|
||||
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_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);
|
||||
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);
|
||||
@ -622,7 +627,7 @@ template <typename T> bool Dqn_VArray_Reserve(Dqn_VArray<T> *array, Dqn_usize co
|
||||
#endif // !defined(DQN_NO_VARRAY)
|
||||
|
||||
#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)
|
||||
{
|
||||
Dqn_SArray<T> result = {};
|
||||
@ -700,7 +705,7 @@ template <typename T> T *Dqn_SArray_Add(Dqn_SArray<T> *array, T const &item)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
return result;
|
||||
@ -896,6 +901,19 @@ Dqn_Slice<T> Dqn_Slice_InitCArray(Dqn_Arena *arena, T const (&array)[N])
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize total_chunks = list->count / (list->chunk_size + (list->chunk_size - 1));
|
||||
Dqn_usize total_chunks = (list->count / list->chunk_size) + ((list->chunk_size % list->count) ? 1 : 0);
|
||||
Dqn_usize desired_chunk = index / list->chunk_size;
|
||||
Dqn_usize forward_scan_dist = desired_chunk;
|
||||
Dqn_usize backward_scan_dist = total_chunks - desired_chunk;
|
||||
@ -1452,11 +1470,11 @@ template <typename T> DQN_API T *Dqn_List_At(Dqn_List<T> *list, Dqn_usize index,
|
||||
Dqn_usize current_chunk = 0;
|
||||
Dqn_ListChunk<T> **chunk = nullptr;
|
||||
if (forward_scan_dist <= backward_scan_dist) {
|
||||
for (chunk = &list->head; *chunk && current_chunk != desired_chunk; *chunk = (*chunk)->next, current_chunk++) {
|
||||
for (chunk = &list->head; *chunk && current_chunk != desired_chunk; chunk = &((*chunk)->next), current_chunk++) {
|
||||
}
|
||||
} else {
|
||||
current_chunk = total_chunks;
|
||||
for (chunk = &list->tail; *chunk && current_chunk != desired_chunk; *chunk = (*chunk)->prev, current_chunk--) {
|
||||
for (chunk = &list->tail; *chunk && current_chunk != desired_chunk; chunk = &((*chunk)->prev), current_chunk--) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
428
dqn_helpers.cpp
428
dqn_helpers.cpp
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -249,222 +252,6 @@ DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, b
|
||||
}
|
||||
#endif // !defined(DQN_NO_JSON_BUILDER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API char const *Dqn_Bin_HexBufferTrim0x(char const *hex, Dqn_usize size, Dqn_usize *real_size)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(hex, size));
|
||||
result = Dqn_Str8_TrimPrefix(result, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive);
|
||||
if (real_size)
|
||||
*real_size = result.size;
|
||||
return result.data;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x(Dqn_Str8 string)
|
||||
{
|
||||
Dqn_usize trimmed_size = 0;
|
||||
char const *trimmed = Dqn_Bin_HexBufferTrim0x(string.data, string.size, &trimmed_size);
|
||||
Dqn_Str8 result = Dqn_Str8_Init(trimmed, trimmed_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8(uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
Dqn_BinHexU64Str8 result = {};
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
result.size += DQN_CAST(int8_t) prefix.size;
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
|
||||
result.size += DQN_CAST(uint8_t) size;
|
||||
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data));
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not, but, always null terminates so
|
||||
// the last byte is wasted.
|
||||
result.size = DQN_MIN(result.size, DQN_ARRAY_UCOUNT(result.data) - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size;
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No);
|
||||
|
||||
if (Dqn_Str8_HasData(result)) {
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
||||
DQN_SNPRINTF(result.data + prefix.size, space, fmt, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_Str8 hex)
|
||||
{
|
||||
Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X"));
|
||||
Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
|
||||
DQN_ASSERT(real_hex.size <= max_hex_size);
|
||||
|
||||
Dqn_usize size = DQN_MIN(max_hex_size, real_hex.size);
|
||||
uint64_t result = 0;
|
||||
for (Dqn_usize index = 0; index < size; index++) {
|
||||
char ch = real_hex.data[index];
|
||||
if (!Dqn_Char_IsHex(ch))
|
||||
break;
|
||||
uint8_t val = Dqn_Char_HexToU8(ch);
|
||||
result = (result << 4) | val;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexPtrToU64(char const *hex, Dqn_usize size)
|
||||
{
|
||||
uint64_t result = Dqn_Bin_HexToU64(Dqn_Str8_Init(hex, size));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer(void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size)
|
||||
{
|
||||
if (!src || !dest)
|
||||
return false;
|
||||
|
||||
if (!DQN_CHECK(dest_size >= src_size * 2))
|
||||
return false;
|
||||
|
||||
char const *HEX = "0123456789abcdef";
|
||||
unsigned char const *src_u8 = DQN_CAST(unsigned char const *) src;
|
||||
for (Dqn_usize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index++] = HEX[(int)hex01];
|
||||
dest[dest_index++] = HEX[(int)hex02];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API char *Dqn_Bin_BytesToHexBufferArena(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
char *result =
|
||||
size > 0 ? Dqn_Arena_NewArray(arena, char, (size * 2) + 1 /*null terminate*/, Dqn_ZeroMem_No) : nullptr;
|
||||
if (result) {
|
||||
bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2);
|
||||
DQN_ASSERT(converted);
|
||||
result[size * 2] = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
result.data = Dqn_Bin_BytesToHexBufferArena(arena, src, size);
|
||||
if (result.data)
|
||||
result.size = size * 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
if (!hex || hex_size <= 0)
|
||||
return result;
|
||||
|
||||
Dqn_usize trim_size = 0;
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, hex_size, &trim_size);
|
||||
|
||||
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
||||
// Either way, the size can be odd or even, hence we round up to the nearest
|
||||
// multiple of two to ensure that we calculate the min buffer size orrectly.
|
||||
Dqn_usize trim_size_rounded_up = trim_size + (trim_size % 2);
|
||||
Dqn_usize min_buffer_size = trim_size_rounded_up / 2;
|
||||
if (dest_size < min_buffer_size || trim_size <= 0) {
|
||||
DQN_ASSERTF(dest_size >= min_buffer_size, "Insufficient buffer size for converting hex to binary");
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex,
|
||||
Dqn_usize hex_size,
|
||||
void *dest,
|
||||
Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest;
|
||||
|
||||
for (Dqn_usize hex_index = 0; hex_index < hex_size; hex_index += 2, result += 1) {
|
||||
char hex01 = hex[hex_index];
|
||||
char hex02 = (hex_index + 1 < hex_size) ? hex[hex_index + 1] : 0;
|
||||
|
||||
char bit4_01 = (hex01 >= '0' && hex01 <= '9') ? 0 + (hex01 - '0')
|
||||
: (hex01 >= 'a' && hex01 <= 'f') ? 10 + (hex01 - 'a')
|
||||
: (hex01 >= 'A' && hex01 <= 'F') ? 10 + (hex01 - 'A')
|
||||
: 0;
|
||||
|
||||
char bit4_02 = (hex02 >= '0' && hex02 <= '9') ? 0 + (hex02 - '0')
|
||||
: (hex02 >= 'a' && hex02 <= 'f') ? 10 + (hex02 - 'a')
|
||||
: (hex02 >= 'A' && hex02 <= 'F') ? 10 + (hex02 - 'A')
|
||||
: 0;
|
||||
|
||||
char byte = (bit4_01 << 4) | (bit4_02 << 0);
|
||||
dest_u8[result] = byte;
|
||||
}
|
||||
|
||||
DQN_ASSERT(result <= dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked(Dqn_Str8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = Dqn_Bin_HexBufferToBytesUnchecked(hex.data, hex.size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes(Dqn_Str8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = Dqn_Bin_HexBufferToBytes(hex.data, hex.size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API char *Dqn_Bin_HexBufferToBytesArena(Dqn_Arena *arena, char const *hex, Dqn_usize size, Dqn_usize *real_size)
|
||||
{
|
||||
char *result = nullptr;
|
||||
if (!arena || !hex || size <= 0)
|
||||
return result;
|
||||
|
||||
Dqn_usize trim_size = 0;
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, size, &trim_size);
|
||||
Dqn_usize binary_size = trim_size / 2;
|
||||
result = Dqn_Arena_NewArray(arena, char, binary_size, Dqn_ZeroMem_No);
|
||||
if (result) {
|
||||
Dqn_usize convert_size = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, result, binary_size);
|
||||
if (real_size)
|
||||
*real_size = convert_size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_Str8 hex)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
result.data = Dqn_Bin_HexBufferToBytesArena(arena, hex.data, hex.size, &result.size);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_Bit_UnsetInplace(Dqn_usize *flags, Dqn_usize bitfield)
|
||||
{
|
||||
@ -1033,6 +820,162 @@ DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_usize type)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_HexToU64(Dqn_Str8 hex)
|
||||
{
|
||||
Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X"));
|
||||
Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
|
||||
DQN_ASSERT(real_hex.size <= max_hex_size);
|
||||
|
||||
Dqn_usize size = DQN_MIN(max_hex_size, real_hex.size);
|
||||
uint64_t result = 0;
|
||||
for (Dqn_usize index = 0; index < size; index++) {
|
||||
char ch = real_hex.data[index];
|
||||
Dqn_CharHexToU8 val = Dqn_Char_HexToU8(ch);
|
||||
if (!val.success)
|
||||
break;
|
||||
result = (result << 4) | val.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size;
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No);
|
||||
|
||||
if (Dqn_Str8_HasData(result)) {
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
||||
DQN_SNPRINTF(result.data + prefix.size, space, fmt, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8(uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
Dqn_U64HexStr8 result = {};
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
result.size += DQN_CAST(int8_t) prefix.size;
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
|
||||
result.size += DQN_CAST(uint8_t) size;
|
||||
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data));
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not, but, always null terminates so
|
||||
// the last byte is wasted.
|
||||
result.size = DQN_MIN(result.size, DQN_ARRAY_UCOUNT(result.data) - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_BytesToHexPtr(void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size)
|
||||
{
|
||||
if (!src || !dest)
|
||||
return false;
|
||||
|
||||
if (!DQN_CHECK(dest_size >= src_size * 2))
|
||||
return false;
|
||||
|
||||
char const *HEX = "0123456789abcdef";
|
||||
unsigned char const *src_u8 = DQN_CAST(unsigned char const *) src;
|
||||
for (Dqn_usize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index++] = HEX[(int)hex01];
|
||||
dest[dest_index++] = HEX[(int)hex02];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_BytesToHex(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!src || size <= 0)
|
||||
return result;
|
||||
|
||||
result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No);
|
||||
result.data[result.size - 1] = 0;
|
||||
bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size);
|
||||
DQN_ASSERT(converted);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked(Dqn_Str8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest;
|
||||
|
||||
for (Dqn_usize hex_index = 0; hex_index < hex.size; hex_index += 2, result += 1) {
|
||||
char hex01 = hex.data[hex_index];
|
||||
char hex02 = (hex_index + 1 < hex.size) ? hex.data[hex_index + 1] : 0;
|
||||
char bit4_01 = Dqn_Char_HexToU8(hex01).value;
|
||||
char bit4_02 = Dqn_Char_HexToU8(hex02).value;
|
||||
char byte = (bit4_01 << 4) | (bit4_02 << 0);
|
||||
dest_u8[result] = byte;
|
||||
}
|
||||
|
||||
DQN_ASSERT(result <= dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtr(Dqn_Str8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x"));
|
||||
hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0X"));
|
||||
|
||||
Dqn_usize result = 0;
|
||||
if (!Dqn_Str8_HasData(hex))
|
||||
return result;
|
||||
|
||||
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
||||
// Either way, the size can be odd or even, hence we round up to the nearest
|
||||
// multiple of two to ensure that we calculate the min buffer size orrectly.
|
||||
Dqn_usize hex_size_rounded_up = hex.size + (hex.size % 2);
|
||||
Dqn_usize min_buffer_size = hex_size_rounded_up / 2;
|
||||
if (hex.size <= 0 || !DQN_CHECK(dest_size >= min_buffer_size)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Dqn_HexToBytesPtrUnchecked(hex, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked(Dqn_Arena *arena, Dqn_Str8 hex)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, hex.size / 2, Dqn_ZeroMem_No);
|
||||
if (result.data) {
|
||||
Dqn_usize bytes_written = Dqn_HexToBytesPtr(hex, result.data, result.size);
|
||||
DQN_ASSERT(bytes_written == result.size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytes(Dqn_Arena *arena, Dqn_Str8 hex)
|
||||
{
|
||||
hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x"));
|
||||
hex = Dqn_Str8_TrimPrefix(hex, DQN_STR8("0X"));
|
||||
|
||||
Dqn_Str8 result = {};
|
||||
if (!DQN_CHECK(Dqn_Str8_IsAll(hex, Dqn_Str8IsAll_Hex)))
|
||||
return result;
|
||||
|
||||
result = Dqn_HexToBytesUnchecked(arena, hex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library ///////////////////////////////////////////////////////////////////////
|
||||
Dqn_Library *g_dqn_library;
|
||||
|
||||
@ -1052,7 +995,13 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
|
||||
if (result->lib_init)
|
||||
return result;
|
||||
result->lib_init = true;
|
||||
|
||||
#define DQN_CPU_FEAT_XENTRY(label) g_dqn_cpu_feature_decl[Dqn_CPUFeature_##label] = {Dqn_CPUFeature_##label, DQN_STR8(#label)};
|
||||
DQN_CPU_FEAT_XMACRO
|
||||
#undef DQN_CPU_FEAT_XENTRY
|
||||
|
||||
result->lib_init = true;
|
||||
result->cpu_report = Dqn_CPU_Report();
|
||||
|
||||
// NOTE: Query OS info /////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
@ -1070,7 +1019,6 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// NOTE Initialise fields //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
result->profiler = &result->profiler_default_instance;
|
||||
@ -1104,10 +1052,9 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
result->exe_dir = Dqn_OS_EXEDir(&result->arena);
|
||||
|
||||
// NOTE: Print out init features ///////////////////////////////////////////////////////////////
|
||||
if (on_init == Dqn_LibraryOnInit_LogFeatures) {
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
if (on_init & Dqn_LibraryOnInit_LogLibFeatures) {
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
|
||||
|
||||
Dqn_f64 page_size_kib = result->os_page_size / 1024.0;
|
||||
@ -1138,10 +1085,37 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
#endif
|
||||
|
||||
// TODO(doyle): Add stacktrace feature log
|
||||
|
||||
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log));
|
||||
}
|
||||
|
||||
if (on_init & Dqn_LibraryOnInit_LogCPUFeatures) {
|
||||
Dqn_CPUReport const *report = &result->cpu_report;
|
||||
Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1));
|
||||
Dqn_Str8Builder_AppendF(&builder,
|
||||
" CPU '%.*s' from '%s' detected:\n",
|
||||
DQN_STR_FMT(brand),
|
||||
report->vendor);
|
||||
|
||||
Dqn_usize longest_feature_name = 0;
|
||||
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
|
||||
Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index];
|
||||
longest_feature_name = DQN_MAX(longest_feature_name, feature_decl.label.size);
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
|
||||
Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index];
|
||||
bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value);
|
||||
Dqn_Str8Builder_AppendF(&builder,
|
||||
" %.*s:%*s%s\n",
|
||||
DQN_STR_FMT(feature_decl.label),
|
||||
DQN_CAST(int)(longest_feature_name - feature_decl.label.size),
|
||||
"",
|
||||
has_feature ? "available" : "not available");
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
if (Dqn_Str8_HasData(info_log))
|
||||
Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1350,7 +1324,7 @@ DQN_API bool Dqn_OS_JobQueueSPMCAddArray(Dqn_JobQueueSPMC *queue, Dqn_Job *jobs,
|
||||
return false;
|
||||
|
||||
for (size_t offset = 0; offset < count; offset++) {
|
||||
uint32_t wrapped_write_index = (write_index + offset) & pot_mask;
|
||||
uint32_t wrapped_write_index = (write_index + offset) & pot_mask;
|
||||
queue->jobs[wrapped_write_index] = jobs[offset];
|
||||
}
|
||||
|
||||
@ -1413,7 +1387,7 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
|
||||
DQN_API void Dqn_OS_JobQueueSPMCWaitForCompletion(Dqn_JobQueueSPMC *queue)
|
||||
{
|
||||
Dqn_OS_MutexLock(&queue->mutex);
|
||||
if (queue->read_index == queue->write_index) {
|
||||
if (queue->finish_index == queue->write_index) {
|
||||
Dqn_OS_MutexUnlock(&queue->mutex);
|
||||
return;
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -50,21 +53,6 @@ struct Dqn_JSONBuilder
|
||||
};
|
||||
#endif // !defined(DQN_NO_JSON_BUIDLER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_BinHexU64Str8
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
enum Dqn_BinHexU64Str8Flags
|
||||
{
|
||||
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
@ -112,7 +100,7 @@ struct Dqn_BinarySearchResult
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_U64Str8
|
||||
{
|
||||
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
|
||||
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separator
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
@ -146,6 +134,18 @@ enum Dqn_U64AgeUnit
|
||||
Dqn_U64AgeUnit_All = Dqn_U64AgeUnit_HMS | Dqn_U64AgeUnit_Day | Dqn_U64AgeUnit_Week | Dqn_U64AgeUnit_Year,
|
||||
};
|
||||
|
||||
struct Dqn_U64HexStr8
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
enum Dqn_U64HexStr8Flags
|
||||
{
|
||||
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
|
||||
@ -182,10 +182,17 @@ struct Dqn_ProfilerZoneScope
|
||||
~Dqn_ProfilerZoneScope();
|
||||
Dqn_ProfilerZone zone;
|
||||
};
|
||||
#define Dqn_Profiler_ZoneScope(name) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), __COUNTER__ + 1)
|
||||
#define Dqn_Profiler_ZoneScopeWithIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index)
|
||||
#define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeWithIndex(name, __COUNTER__ + 1)
|
||||
#endif
|
||||
|
||||
#define Dqn_Profiler_ZoneBlockIndex(name, index) \
|
||||
for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneWithIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \
|
||||
DQN_UNIQUE_NAME(dummy__).begin_tsc == 0; \
|
||||
Dqn_Profiler_EndZone(DQN_UNIQUE_NAME(profile_zone__)), DQN_UNIQUE_NAME(dummy__).begin_tsc = 1)
|
||||
|
||||
#define Dqn_Profiler_ZoneBlock(name) Dqn_Profiler_ZoneBlockIndex(DQN_STR8(name), __COUNTER__ + 1)
|
||||
|
||||
enum Dqn_ProfilerAnchorBuffer
|
||||
{
|
||||
Dqn_ProfilerAnchorBuffer_Back,
|
||||
@ -207,10 +214,13 @@ struct Dqn_Job
|
||||
bool add_to_completion_queue;
|
||||
Dqn_Arena *arena;
|
||||
Dqn_JobQueueFunc *func;
|
||||
uint32_t user_tag;
|
||||
void *user_context;
|
||||
};
|
||||
|
||||
#if !defined(DQN_JOB_QUEUE_SPMC_SIZE)
|
||||
#define DQN_JOB_QUEUE_SPMC_SIZE 128
|
||||
#endif
|
||||
|
||||
struct Dqn_JobQueueSPMC
|
||||
{
|
||||
Dqn_OSMutex mutex;
|
||||
@ -218,7 +228,7 @@ struct Dqn_JobQueueSPMC
|
||||
Dqn_OSSemaphore wait_for_completion_semaphore;
|
||||
uint32_t threads_waiting_for_completion;
|
||||
|
||||
Dqn_Job jobs[64];
|
||||
Dqn_Job jobs[DQN_JOB_QUEUE_SPMC_SIZE];
|
||||
Dqn_b32 quit;
|
||||
uint32_t quit_exit_code;
|
||||
uint32_t volatile read_index;
|
||||
@ -226,7 +236,7 @@ struct Dqn_JobQueueSPMC
|
||||
uint32_t volatile write_index;
|
||||
|
||||
Dqn_OSSemaphore complete_queue_write_semaphore;
|
||||
Dqn_Job complete_queue[64];
|
||||
Dqn_Job complete_queue[DQN_JOB_QUEUE_SPMC_SIZE];
|
||||
uint32_t volatile complete_read_index;
|
||||
uint32_t volatile complete_write_index;
|
||||
};
|
||||
@ -238,11 +248,13 @@ struct Dqn_Library
|
||||
{
|
||||
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
|
||||
Dqn_TicketMutex lib_mutex;
|
||||
Dqn_TicketMutex thread_context_init_mutex;
|
||||
Dqn_Str8 exe_dir; // The directory of the current executable
|
||||
Dqn_Arena arena;
|
||||
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
|
||||
Dqn_ArenaCatalog arena_catalog;
|
||||
bool slow_verification_checks; // Enable expensive library verification checks
|
||||
Dqn_CPUReport cpu_report;
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data;
|
||||
@ -275,8 +287,10 @@ struct Dqn_Library
|
||||
|
||||
enum Dqn_LibraryOnInit
|
||||
{
|
||||
Dqn_LibraryOnInit_Nil,
|
||||
Dqn_LibraryOnInit_LogFeatures,
|
||||
Dqn_LibraryOnInit_Nil = 0,
|
||||
Dqn_LibraryOnInit_LogLibFeatures = 1 << 0,
|
||||
Dqn_LibraryOnInit_LogCPUFeatures = 1 << 1,
|
||||
Dqn_LibraryOnInit_LogAllFeatures = Dqn_LibraryOnInit_LogLibFeatures | Dqn_LibraryOnInit_LogCPUFeatures,
|
||||
};
|
||||
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
@ -331,32 +345,6 @@ DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBu
|
||||
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value)
|
||||
#endif // !defined(DQN_NO_JSON_BUILDER)
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
// TODO(doyle): I'm not happy with this API. Its ugly and feels complicated
|
||||
// because I designed it as a C-API first (e.g. ptr + length) vs just accepting
|
||||
// that Dqn_Str8/Dqn_Slice is a superior API to design for first.
|
||||
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex);
|
||||
DQN_API uint64_t Dqn_Bin_HexPtrToU64 (char const *hex, Dqn_usize size);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <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 bool Dqn_Bit_IsSet (Dqn_usize bits, Dqn_usize bits_to_set);
|
||||
DQN_API bool Dqn_Bit_IsNotSet (Dqn_usize bits, Dqn_usize bits_to_check);
|
||||
#define Dqn_Bit_ClearNextLSB(value) (value) & ((value) - 1)
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
|
||||
@ -439,6 +428,18 @@ DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena
|
||||
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type);
|
||||
|
||||
DQN_API uint64_t Dqn_HexToU64 (Dqn_Str8 hex);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8 (uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API bool Dqn_BytesToHexPtr (Dqn_Arena *arena, void const *src, Dqn_usize src_size, char *dest);
|
||||
DQN_API Dqn_Str8 Dqn_BytesToHex (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek);
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek);
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytes (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1)
|
||||
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index);
|
||||
|
429
dqn_json.cpp
Normal file
429
dqn_json.cpp
Normal 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);
|
||||
}
|
||||
}
|
436
dqn_json.h
436
dqn_json.h
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
#if !defined(SHEREDOM_JSON_H_INCLUDED)
|
||||
#error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_JSON_H)
|
||||
#define DQN_JSON_H
|
||||
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count);
|
||||
@ -86,434 +87,3 @@ bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it);
|
||||
|
||||
#define Dqn_JSON_ItErrorUnknownKeyValue(it) Dqn_JSON_ItErrorUnknownKeyValue_(it, DQN_CALL_SITE)
|
||||
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site);
|
||||
|
||||
#endif // DQN_JSON_H
|
||||
|
||||
#if defined(DQN_JSON_IMPLEMENTATION)
|
||||
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (!user_data)
|
||||
return result;
|
||||
|
||||
Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data;
|
||||
result = Dqn_Arena_Alloc(arena, count, alignof(json_value_s), Dqn_ZeroMem_No);
|
||||
return result;
|
||||
}
|
||||
|
||||
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size)
|
||||
{
|
||||
switch (type) {
|
||||
case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; }
|
||||
case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; }
|
||||
case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; }
|
||||
case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; }
|
||||
case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; }
|
||||
case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; }
|
||||
|
||||
default: /*FALLTHRU*/
|
||||
case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; }
|
||||
}
|
||||
}
|
||||
|
||||
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_Str8 key)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs && Dqn_Str8_HasData(key)) {
|
||||
Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size);
|
||||
result = Dqn_Str8_Eq(lhs_string, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_It ///////////////////////////////////////////////////////////////////////////////
|
||||
Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json)
|
||||
{
|
||||
json_parse_result_s parse_result = {};
|
||||
json_value_ex_s *ex_value =
|
||||
DQN_CAST(json_value_ex_s *) json_parse_ex(json.data,
|
||||
json.size,
|
||||
json_parse_flags_allow_location_information,
|
||||
Dqn_JSON_ArenaAllocFunc,
|
||||
arena,
|
||||
&parse_result);
|
||||
|
||||
Dqn_JSONIt result = {};
|
||||
Dqn_JSON_ItPushValue(&result, &ex_value->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_ItPush/Pop ///////////////////////////////////////////////////////////////////////
|
||||
bool Dqn_JSON_ItPushObjElement(Dqn_JSONIt *it, json_object_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObjElement, element};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItPushObj(Dqn_JSONIt *it, json_object_s *obj)
|
||||
{
|
||||
if (!it || !obj)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObj, obj};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArrayElement, element};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItPushArray(Dqn_JSONIt *it, json_value_s *value)
|
||||
{
|
||||
if (!it || !value || json_value_as_array(value) == nullptr)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArray, value};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItPushValue(Dqn_JSONIt *it, json_value_s *value)
|
||||
{
|
||||
bool result = false;
|
||||
if (!it || !value)
|
||||
return result;
|
||||
|
||||
if (value->type == json_type_object) {
|
||||
result = Dqn_JSON_ItPushObj(it, json_value_as_object(value));
|
||||
} else if (value->type == json_type_array) {
|
||||
result = Dqn_JSON_ItPushArray(it, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_JSON_ItPop(Dqn_JSONIt *it)
|
||||
{
|
||||
if (!it)
|
||||
return;
|
||||
DQN_ASSERT(it->stack_count > 0);
|
||||
if (it->stack_count > 0)
|
||||
it->stack_count--;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_It JSON tree navigation //////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *result = nullptr;
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return result;
|
||||
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value;
|
||||
result = element->value;
|
||||
} else {
|
||||
result = DQN_CAST(json_value_s *) curr->value;
|
||||
}
|
||||
|
||||
if (result->type == json_type_array) {
|
||||
json_array_s *array = json_value_as_array(result);
|
||||
DQN_ASSERT(array);
|
||||
Dqn_JSON_ItPushArray(it, result);
|
||||
} else if (result->type == json_type_object) {
|
||||
json_object_s *obj = json_value_as_object(result);
|
||||
DQN_ASSERT(obj);
|
||||
Dqn_JSON_ItPushObj(it, obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItNext(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return false;
|
||||
|
||||
json_object_element_s *obj_element = nullptr;
|
||||
json_array_element_s *array_element = nullptr;
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObj) {
|
||||
auto *obj = DQN_CAST(json_object_s *) curr->value;
|
||||
obj_element = obj->start;
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
auto *element = DQN_CAST(json_object_element_s *) curr->value;
|
||||
obj_element = element->next;
|
||||
Dqn_JSON_ItPop(it);
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArray) {
|
||||
auto *value = DQN_CAST(json_value_s *) curr->value;
|
||||
auto *array = json_value_as_array(value);
|
||||
array_element = array->start;
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
auto *element = DQN_CAST(json_array_element_s *) curr->value;
|
||||
array_element = element->next;
|
||||
Dqn_JSON_ItPop(it);
|
||||
} else {
|
||||
Dqn_JSON_ItPop(it);
|
||||
}
|
||||
|
||||
if (obj_element)
|
||||
Dqn_JSON_ItPushObjElement(it, obj_element);
|
||||
else if (array_element)
|
||||
Dqn_JSON_ItPushArrayElement(it, array_element);
|
||||
|
||||
bool result = obj_element || array_element;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_ItCurr ///////////////////////////////////////////////////////////////////////////
|
||||
Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONItEntry *result = nullptr;
|
||||
if (!it || it->stack_count <= 0)
|
||||
return result;
|
||||
|
||||
result = &it->stack[it->stack_count - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *result = nullptr;
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return result;
|
||||
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
auto *element = DQN_CAST(json_object_element_s *)curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
auto *element = DQN_CAST(json_array_element_s *)curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeString ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeNumber ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeObj ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeArray)
|
||||
{
|
||||
result = DQN_CAST(json_value_s *)curr->value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement)
|
||||
? DQN_CAST(json_object_element_s *) curr->value
|
||||
: nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_ItValueIs ////////////////////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_number_s *result = curr ? json_value_as_number(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it)
|
||||
{
|
||||
size_t result = 0;
|
||||
if (json_array_s *curr = Dqn_JSON_ItValueIsArray(it))
|
||||
result = curr->length;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_JSON_ItKeyValueIs /////////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
Dqn_Str8 result = {};
|
||||
if (curr) {
|
||||
result.data = DQN_CAST(char *)curr->name->string;
|
||||
result.size = curr->name->string_size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
bool result = Dqn_JSON_String8Cmp(curr->name, key);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_s *result = nullptr;
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_object(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_array_s *result = nullptr;
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_array(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_string_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_string(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_number_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_number(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_value_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_value_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = curr->value->type == json_type_null ? curr->value : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_JSON_ItValueTo ////////////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (json_string_s *curr = Dqn_JSON_ItValueIsString(it))
|
||||
result = Dqn_Str8_Init(curr->string, curr->string_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it)
|
||||
{
|
||||
int64_t result = {};
|
||||
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
|
||||
result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it)
|
||||
{
|
||||
uint64_t result = {};
|
||||
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
|
||||
result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it)
|
||||
{
|
||||
bool result = {};
|
||||
if (json_value_s *curr = Dqn_JSON_ItValueIsBool(it))
|
||||
result = curr->type == json_type_true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site)
|
||||
{
|
||||
if (!it)
|
||||
return;
|
||||
|
||||
json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (!curr)
|
||||
return;
|
||||
|
||||
size_t value_type_size = 0;
|
||||
char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)curr->value->type, &value_type_size);
|
||||
|
||||
json_string_s const *key = curr->name;
|
||||
if (it->flags & json_parse_flags_allow_location_information) {
|
||||
json_string_ex_s const *info = DQN_CAST(json_string_ex_s const *)key;
|
||||
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
|
||||
call_site,
|
||||
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
|
||||
info->line_no,
|
||||
info->row_no,
|
||||
DQN_CAST(int)key->string_size,
|
||||
key->string,
|
||||
DQN_CAST(int)value_type_size,
|
||||
value_type);
|
||||
} else {
|
||||
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
|
||||
call_site,
|
||||
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
|
||||
DQN_CAST(int)key->string_size,
|
||||
key->string,
|
||||
DQN_CAST(int)value_type_size,
|
||||
value_type);
|
||||
}
|
||||
}
|
||||
#endif // defined(DQN_JSON_IMPLEMENTATION)
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -75,7 +78,7 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena);
|
||||
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
result = Dqn_Str8_Copy(arena, split.lhs);
|
||||
return result;
|
||||
}
|
||||
|
21
dqn_os.h
21
dqn_os.h
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -222,17 +225,6 @@ enum Dqn_OSSemaphoreWaitResult
|
||||
};
|
||||
#endif // !defined(DQN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSMutex
|
||||
{
|
||||
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
char win32_handle[48];
|
||||
#else
|
||||
pthread_mutex_t posix_handle;
|
||||
pthread_mutexattr_t posix_attribs;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
|
||||
typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*);
|
||||
@ -295,6 +287,7 @@ DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow ();
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 (Dqn_OSDateTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API uint64_t Dqn_OS_DateUnixTime ();
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate (uint64_t time);
|
||||
DQN_API uint64_t Dqn_OS_DateToUnixTime (Dqn_OSDateTime date);
|
||||
DQN_API bool Dqn_OS_DateIsValid (Dqn_OSDateTime date);
|
||||
|
||||
@ -378,10 +371,10 @@ DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait (Dqn_OSSemaphore *sem
|
||||
#endif // !defined(DQN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSMutex Dqn_OS_MutexInit (uint32_t initial_count, uint32_t max_count);
|
||||
DQN_API Dqn_OSMutex Dqn_OS_MutexInit ();
|
||||
DQN_API void Dqn_OS_MutexDeinit(Dqn_OSMutex *mutex);
|
||||
DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex mutex);
|
||||
DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex mutex);
|
||||
DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex *mutex);
|
||||
DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex);
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -137,6 +140,20 @@ DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate(uint64_t time)
|
||||
{
|
||||
time_t posix_time = DQN_CAST(time_t) time;
|
||||
struct tm posix_date = *gmtime(&posix_time);
|
||||
Dqn_OSDateTime result = {};
|
||||
result.year = posix_date.tm_year + 1900;
|
||||
result.month = posix_date.tm_mon + 1;
|
||||
result.day = posix_date.tm_mday;
|
||||
result.hour = posix_date.tm_hour;
|
||||
result.minutes = posix_date.tm_min;
|
||||
result.seconds = posix_date.tm_sec;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size)
|
||||
{
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
@ -127,6 +130,30 @@ DQN_API uint64_t Dqn_OS_DateUnixTime()
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate(uint64_t time)
|
||||
{
|
||||
// NOTE: Windows epoch time starts from Jan 1, 1601 and counts in
|
||||
// 100-nanoseconds intervals.
|
||||
//
|
||||
// See: https://devblogs.microsoft.com/oldnewthing/20090306-00/?p=18913
|
||||
|
||||
uint64_t win32_time = 116'444'736'000'000'000 + (time * 10'000'000);
|
||||
SYSTEMTIME sys_time = {};
|
||||
FILETIME file_time = {};
|
||||
file_time.dwLowDateTime = (DWORD)win32_time;
|
||||
file_time.dwHighDateTime = win32_time >> 32;
|
||||
FileTimeToSystemTime(&file_time, &sys_time);
|
||||
|
||||
Dqn_OSDateTime result = {};
|
||||
result.year = DQN_CAST(uint16_t)sys_time.wYear;
|
||||
result.month = DQN_CAST(uint8_t)sys_time.wMonth;
|
||||
result.day = DQN_CAST(uint8_t)sys_time.wDay;
|
||||
result.hour = DQN_CAST(uint8_t)sys_time.wHour;
|
||||
result.minutes = DQN_CAST(uint8_t)sys_time.wMinute;
|
||||
result.seconds = DQN_CAST(uint8_t)sys_time.wSecond;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date)
|
||||
{
|
||||
DQN_ASSERT(Dqn_OS_DateIsValid(date));
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -156,7 +159,7 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult result = {};
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
@ -179,9 +182,9 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 stri
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, Dqn_Str8 find)
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast(Dqn_Str8 string, Dqn_Str8 find)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitReverseArray(string, &find, 1);
|
||||
Dqn_Str8BinarySplitResult result = Dqn_Str8_BinarySplitLastArray(string, &find, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -218,7 +221,7 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
{
|
||||
Dqn_Str8FindResult result = {};
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
@ -241,13 +244,13 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_St
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString(Dqn_Str8 string, Dqn_Str8 find)
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find)
|
||||
{
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindFirstStringArray(string, &find, 1);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst(Dqn_Str8 string, uint32_t flags)
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_Find(Dqn_Str8 string, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8FindResult result = {};
|
||||
for (size_t index = 0; !result.found && index < string.size; index++) {
|
||||
@ -457,7 +460,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
Dqn_Str8 result = Dqn_Str8_HasData(split.rhs) ? split.rhs : split.lhs;
|
||||
return result;
|
||||
}
|
||||
@ -471,14 +474,14 @@ DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension(Dqn_Str8 path)
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8("."));
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLast(path, DQN_STR8("."));
|
||||
Dqn_Str8 result = split.lhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileExtension(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8("."));
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLast(path, DQN_STR8("."));
|
||||
Dqn_Str8 result = split.rhs;
|
||||
return result;
|
||||
}
|
||||
@ -895,17 +898,18 @@ DQN_API bool Dqn_Char_IsHex(char ch)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Char_HexToU8(char ch)
|
||||
DQN_API Dqn_CharHexToU8 Dqn_Char_HexToU8(char ch)
|
||||
{
|
||||
DQN_ASSERTF(Dqn_Char_IsHex(ch), "Hex character not valid '%c'", ch);
|
||||
|
||||
uint8_t result = 0;
|
||||
Dqn_CharHexToU8 result = {};
|
||||
result.success = true;
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
result = ch - 'a' + 10;
|
||||
result.value = ch - 'a' + 10;
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
result = ch - 'A' + 10;
|
||||
result.value = ch - 'A' + 10;
|
||||
else if (ch >= '0' && ch <= '9')
|
||||
result.value = ch - '0';
|
||||
else
|
||||
result = ch - '0';
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
21
dqn_string.h
21
dqn_string.h
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -159,14 +162,14 @@ DQN_API Dqn_Str8 Dqn_Str8_Advance
|
||||
DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
|
||||
@ -234,12 +237,18 @@ template <Dqn_usize N> bool operator!=
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char //////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_CharHexToU8
|
||||
{
|
||||
bool success;
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_Char_IsAlphabet (char ch);
|
||||
DQN_API bool Dqn_Char_IsDigit (char ch);
|
||||
DQN_API bool Dqn_Char_IsAlphaNum (char ch);
|
||||
DQN_API bool Dqn_Char_IsWhitespace (char ch);
|
||||
DQN_API bool Dqn_Char_IsHex (char ch);
|
||||
DQN_API uint8_t Dqn_Char_HexToU8 (char ch);
|
||||
DQN_API Dqn_CharHexToU8 Dqn_Char_HexToU8 (char ch);
|
||||
DQN_API char Dqn_Char_ToHex (char ch);
|
||||
DQN_API char Dqn_Char_ToHexUnchecked (char ch);
|
||||
DQN_API char Dqn_Char_ToLower (char ch);
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -58,6 +61,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
||||
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
|
||||
|
||||
// NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex);
|
||||
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
|
||||
|
||||
// NOTE: We allocate arenas so that they all come from the memory
|
||||
@ -100,7 +104,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
||||
}
|
||||
result->error_sink.arena = result->error_sink_arena;
|
||||
}
|
||||
|
||||
Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -30,6 +33,7 @@ struct Dqn_TypeField
|
||||
Dqn_isize value;
|
||||
Dqn_usize offset_of;
|
||||
Dqn_usize size_of;
|
||||
Dqn_usize align_of;
|
||||
Dqn_Str8 type_decl;
|
||||
uint32_t type_enum;
|
||||
bool is_pointer;
|
||||
|
@ -3,6 +3,333 @@
|
||||
|
||||
#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()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
@ -104,79 +431,79 @@ static Dqn_UTest Dqn_Test_Bin()
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Bin") {
|
||||
DQN_UTEST_TEST("Convert 0x123") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0x123"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("0x123"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0x123, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert 0xFFFF") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xFFFF"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("0xFFFF"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert FFFF") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("FFFF"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("FFFF"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0xFFFF, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert abCD") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("abCD"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("abCD"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert 0xabCD") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0xabCD"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("0xabCD"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0xabCD, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert 0x") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0x"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("0x"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert 0X") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("0X"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("0X"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0x0, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert 3") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("3"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("3"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 3, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert f") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("f"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("f"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0xf, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert g") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("g"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("g"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Convert -0x3") {
|
||||
uint64_t result = Dqn_Bin_HexToU64(DQN_STR8("-0x3"));
|
||||
uint64_t result = Dqn_HexToU64(DQN_STR8("-0x3"));
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %" PRIu64, result);
|
||||
}
|
||||
|
||||
uint32_t number = 0xd095f6;
|
||||
DQN_UTEST_TEST("Convert %x to string", number) {
|
||||
Dqn_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number));
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
|
||||
}
|
||||
|
||||
number = 0xf6ed00;
|
||||
DQN_UTEST_TEST("Convert %x to string", number) {
|
||||
Dqn_Str8 number_hex = Dqn_Bin_BytesToHexArena(scratch.arena, &number, sizeof(number));
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("00edf600")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
|
||||
}
|
||||
|
||||
Dqn_Str8 hex = DQN_STR8("0xf6ed00");
|
||||
DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) {
|
||||
Dqn_Str8 bytes = Dqn_Bin_HexToBytesArena(scratch.arena, hex);
|
||||
Dqn_Str8 bytes = Dqn_HexToBytes(scratch.arena, hex);
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")),
|
||||
"number_hex=%.*s",
|
||||
DQN_STR_FMT(Dqn_Bin_BytesToHexArena(scratch.arena, bytes.data, bytes.size)));
|
||||
DQN_STR_FMT(Dqn_BytesToHex(scratch.arena, bytes.data, bytes.size)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -975,7 +1302,7 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] =
|
||||
void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 input_hex = Dqn_Bin_BytesToHexArena(scratch.arena, input.data, input.size);
|
||||
Dqn_Str8 input_hex = Dqn_BytesToHex(scratch.arena, input.data, input.size);
|
||||
|
||||
switch(hash_type)
|
||||
{
|
||||
@ -1619,12 +1946,11 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %" PRIu64, result.value);
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Str8_Find
|
||||
// =========================================================================================
|
||||
// NOTE: Dqn_Str8_Find /////////////////////////////////////////////////////////////////////
|
||||
DQN_UTEST_TEST("Find: String (char) is not in buffer") {
|
||||
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
|
||||
Dqn_Str8 find = DQN_STR8("2");
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find);
|
||||
DQN_UTEST_ASSERT(&test, !result.found);
|
||||
DQN_UTEST_ASSERT(&test, result.index == 0);
|
||||
DQN_UTEST_ASSERT(&test, result.match.data == nullptr);
|
||||
@ -1634,14 +1960,13 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_TEST("Find: String (char) is in buffer") {
|
||||
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
|
||||
Dqn_Str8 find = DQN_STR8("6");
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindFirstString(buf, find);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find);
|
||||
DQN_UTEST_ASSERT(&test, result.found);
|
||||
DQN_UTEST_ASSERT(&test, result.index == 2);
|
||||
DQN_UTEST_ASSERT(&test, result.match.data[0] == '6');
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Str8_FileNameFromPath
|
||||
// =========================================================================================
|
||||
// NOTE: Dqn_Str8_FileNameFromPath /////////////////////////////////////////////////////////
|
||||
DQN_UTEST_TEST("File name from Windows path") {
|
||||
Dqn_Str8 buf = DQN_STR8("C:\\ABC\\test.exe");
|
||||
Dqn_Str8 result = Dqn_Str8_FileNameFromPath(buf);
|
||||
@ -1897,6 +2222,7 @@ void Dqn_Test_RunSuite()
|
||||
{
|
||||
Dqn_UTest tests[] =
|
||||
{
|
||||
Dqn_Test_Base(),
|
||||
Dqn_Test_Arena(),
|
||||
Dqn_Test_Bin(),
|
||||
Dqn_Test_BinarySearch(),
|
||||
@ -1933,7 +2259,7 @@ void Dqn_Test_RunSuite()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argv; (void)argc;
|
||||
Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures);
|
||||
Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures);
|
||||
Dqn_Test_RunSuite();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
|
17
readme.md
17
readme.md
@ -9,21 +9,8 @@ separated by category into files for organisation. You only need to include
|
||||
## Build
|
||||
|
||||
To build with this library, copy all the `*.[h|cpp]` files at the root of the
|
||||
repository to your desired location, accessible by your project and in one
|
||||
header file include the header.
|
||||
|
||||
```cpp
|
||||
#include "dqn.h"
|
||||
```
|
||||
|
||||
`dqn.h` includes all other files and their declaration into your header. In
|
||||
*one* `.cpp` file define the macro to enable the implementation of the header in
|
||||
that translation unit.
|
||||
|
||||
```cpp
|
||||
#define DQN_IMPLEMENTATION
|
||||
#include "dqn.h"
|
||||
```
|
||||
repository to your desired location and compile `dqn.cpp` or include it into one
|
||||
of your translation units.
|
||||
|
||||
Finally ensure that the compiler has in its search paths for the include
|
||||
directory where headers are located, e.g. `-I <path/to/dqn/headers>`.
|
||||
|
Loading…
Reference in New Issue
Block a user