Pull in bug fixes from upstream

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

View File

@ -13,7 +13,7 @@ pushd Build
REM O2 Optimisation Level 2
REM 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

View File

@ -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
View File

@ -0,0 +1,62 @@
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
// $$ | $$ |\$ /$$ |$$ | $$ |
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
// \______|\__| \__|\__| \________|
//
// Implementation
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
#if defined(DQN_WITH_CGEN)
#if !defined(DQN_NO_METADESK)
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<function>': unreferenced function with internal linkage has been removed
#include "External/metadesk/md.c"
DQN_MSVC_WARNING_POP
#endif
#define DQN_CPP_FILE_IMPLEMENTATION
#include "Standalone/dqn_cpp_file.h"
#include "dqn_cgen.cpp"
#endif
#if defined(DQN_WITH_JSON)
#include "dqn_json.cpp"
#endif
#include "dqn_base.cpp"
#include "dqn_thread_context.cpp"
#include "dqn_external.cpp"
#include "dqn_allocator.cpp"
#include "dqn_debug.cpp"
#include "dqn_string.cpp"
#include "dqn_containers.cpp"
#include "dqn_type_info.cpp"
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
#include "dqn_os_posix.cpp"
#elif defined(DQN_PLATFORM_WIN32)
#include "dqn_os_win32.cpp"
#else
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
#include "dqn_os.cpp"
#include "dqn_math.cpp"
#include "dqn_hash.cpp"
#include "dqn_helpers.cpp"
#if defined(DQN_WITH_UNIT_TESTS)
#include "dqn_unit_tests.cpp"
#endif
#include "dqn_docs.cpp"

79
dqn.h
View File

@ -1,4 +1,4 @@
#if !defined(DQN_H)
#pragma once
#define DQN_H
/*
@ -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

View File

@ -345,7 +345,8 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
// NOTE: Store the offset to the original pointer behind the user's
// 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;

View File

@ -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
View File

@ -0,0 +1,286 @@
#pragma once
#include "dqn.h"
#include <immintrin.h>
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// /$$$$$$ /$$ /$$ /$$ /$$ /$$$$$$$ /$$ /$$$$$$ /$$$$$$$$
// /$$__ $$| $$ | $$| $$ / $$ | $$____/ /$$$$ /$$__ $$| $$_____/
// | $$ \ $$| $$ | $$| $$/ $$/ | $$ |_ $$ |__/ \ $$| $$
// | $$$$$$$$| $$ / $$/ \ $$$$/ /$$$$$$| $$$$$$$ | $$ /$$$$$$/| $$$$$
// | $$__ $$ \ $$ $$/ >$$ $$|______/|_____ $$ | $$ /$$____/ | $$__/
// | $$ | $$ \ $$$/ /$$/\ $$ /$$ \ $$ | $$ | $$ | $$
// | $$ | $$ \ $/ | $$ \ $$ | $$$$$$//$$$$$$| $$$$$$$$| $$
// |__/ |__/ \_/ |__/ |__/ \______/|______/|________/|__/
//
// dqn_avx512f.h
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8AVX512F(Dqn_Str8 string, Dqn_Str8 find)
{
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
Dqn_Str8FindResult result = {};
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size)
return result;
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
Dqn_usize const search_size = string.size - find.size;
Dqn_usize simd_iterations = search_size / sizeof(__m512i);
char const *ptr = string.data;
while (simd_iterations--) {
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
// where matches are found.
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
// operation using the ternarylogic intrinsic.
//
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
// B = find_last_ch_block
// C = find_last_ch
//
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
//
// / A / B / C / B ^ C / A | (B ^ C) /
// | 0 | 0 | 0 | 0 | 0 |
// | 0 | 0 | 1 | 1 | 1 |
// | 0 | 1 | 0 | 1 | 1 |
// | 0 | 1 | 1 | 0 | 0 |
// | 1 | 0 | 0 | 0 | 1 |
// | 1 | 0 | 1 | 1 | 1 |
// | 1 | 1 | 0 | 1 | 1 |
// | 1 | 1 | 1 | 0 | 1 |
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
// out which 32 bit elements in the vector had zero bytes. This uses a
// bit twiddling trick
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
__mmask16 zero_byte_mask = {};
{
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
}
while (zero_byte_mask) {
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
char const *base_ptr = ptr + (4 * lsb_zero_pos);
if (DQN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data;
} else if (DQN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 1;
} else if (DQN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 2;
} else if (DQN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 3;
}
if (result.found) {
result.start_to_before_match = Dqn_Str8_Init(string.data, result.index);
result.match = Dqn_Str8_Init(string.data + result.index, find.size);
result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - result.index);
return result;
}
zero_byte_mask = Dqn_Bit_ClearNextLSB(zero_byte_mask);
}
ptr += sizeof(__m512i);
}
for (Dqn_usize index = ptr - string.data; index < string.size; index++) {
Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find.size);
if (Dqn_Str8_Eq(string_slice, find)) {
result.found = true;
result.index = index;
result.start_to_before_match = Dqn_Str8_Init(string.data, index);
result.match = Dqn_Str8_Init(string.data + index, find.size);
result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index);
return result;
}
}
return result;
}
DQN_API Dqn_Str8FindResult Dqn_Str8_FindLastStr8AVX512F(Dqn_Str8 string, Dqn_Str8 find)
{
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
Dqn_Str8FindResult result = {};
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size)
return result;
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
Dqn_usize const search_size = string.size - find.size;
Dqn_usize simd_iterations = search_size / sizeof(__m512i);
char const *ptr = string.data + search_size + 1;
while (simd_iterations--) {
ptr -= sizeof(__m512i);
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
// where matches are found.
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
// operation using the ternarylogic intrinsic.
//
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
// B = find_last_ch_block
// C = find_last_ch
//
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
//
// / A / B / C / B ^ C / A | (B ^ C) /
// | 0 | 0 | 0 | 0 | 0 |
// | 0 | 0 | 1 | 1 | 1 |
// | 0 | 1 | 0 | 1 | 1 |
// | 0 | 1 | 1 | 0 | 0 |
// | 1 | 0 | 0 | 0 | 1 |
// | 1 | 0 | 1 | 1 | 1 |
// | 1 | 1 | 0 | 1 | 1 |
// | 1 | 1 | 1 | 0 | 1 |
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
// out which 32 bit elements in the vector had zero bytes. This uses a
// bit twiddling trick
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
__mmask16 zero_byte_mask = {};
{
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
}
while (zero_byte_mask) {
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
char const *base_ptr = ptr + (4 * lsb_zero_pos);
if (DQN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data;
} else if (DQN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 1;
} else if (DQN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 2;
} else if (DQN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
result.found = true;
result.index = base_ptr - string.data + 3;
}
if (result.found) {
result.start_to_before_match = Dqn_Str8_Init(string.data, result.index);
result.match = Dqn_Str8_Init(string.data + result.index, find.size);
result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - result.index);
return result;
}
zero_byte_mask = Dqn_Bit_ClearNextLSB(zero_byte_mask);
}
}
for (Dqn_usize index = ptr - string.data - 1; index < string.size; index--) {
Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find.size);
if (Dqn_Str8_Eq(string_slice, find)) {
result.found = true;
result.index = index;
result.start_to_before_match = Dqn_Str8_Init(string.data, index);
result.match = Dqn_Str8_Init(string.data + index, find.size);
result.match_to_end_of_buffer = Dqn_Str8_Init(result.match.data, string.size - index);
return result;
}
}
return result;
}
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitAVX512F(Dqn_Str8 string, Dqn_Str8 find)
{
Dqn_Str8BinarySplitResult result = {};
Dqn_Str8FindResult find_result = Dqn_Str8_FindStr8AVX512F(string, find);
if (find_result.found) {
result.lhs.data = string.data;
result.lhs.size = find_result.index;
result.rhs = Dqn_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
} else {
result.lhs = string;
}
return result;
}
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastAVX512F(Dqn_Str8 string, Dqn_Str8 find)
{
Dqn_Str8BinarySplitResult result = {};
Dqn_Str8FindResult find_result = Dqn_Str8_FindLastStr8AVX512F(string, find);
if (find_result.found) {
result.lhs.data = string.data;
result.lhs.size = find_result.index;
result.rhs = Dqn_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
} else {
result.lhs = string;
}
return result;
}
DQN_API Dqn_usize Dqn_Str8_SplitAVX512F(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode)
{
Dqn_usize result = 0; // The number of splits in the actual string.
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(delimiter) || delimiter.size <= 0)
return result;
Dqn_Str8BinarySplitResult split = {};
Dqn_Str8 first = string;
do {
split = Dqn_Str8_BinarySplitAVX512F(first, delimiter);
if (split.lhs.size || mode == Dqn_Str8SplitIncludeEmptyStrings_Yes) {
if (splits && result < splits_count)
splits[result] = split.lhs;
result++;
}
first = split.rhs;
} while (first.size);
return result;
}
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAllocAVX512F(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode)
{
Dqn_Slice<Dqn_Str8> result = {};
Dqn_usize splits_required = Dqn_Str8_SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
result.data = Dqn_Arena_NewArray(arena, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
if (result.data) {
result.size = Dqn_Str8_SplitAVX512F(string, delimiter, result.data, splits_required, mode);
DQN_ASSERT(splits_required == result.size);
}
return result;
}

30
dqn_avx512f.h Normal file
View File

@ -0,0 +1,30 @@
#if !defined(DQN_AVX512F_H)
#define DQN_AVX512F_H
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$$\
// $$ __$$\ $$ | $$ |$$ | $$ | $$ ____| $$$$ | $$ __$$\ $$ _____|
// $$ / $$ |$$ | $$ |\$$\ $$ | $$ | \_$$ | \__/ $$ |$$ |
// $$$$$$$$ |\$$\ $$ | \$$$$ /$$$$$$\ $$$$$$$\ $$ | $$$$$$ |$$$$$\
// $$ __$$ | \$$\$$ / $$ $$< \______|\_____$$\ $$ | $$ ____/ $$ __|
// $$ | $$ | \$$$ / $$ /\$$\ $$\ $$ | $$ | $$ | $$ |
// $$ | $$ | \$ / $$ / $$ | \$$$$$$ |$$$$$$\ $$$$$$$$\ $$ |
// \__| \__| \_/ \__| \__| \______/ \______|\________|\__|
//
// dqn_avx512f.h -- Functions implemented w/ AVX512
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
#include "dqn.h"
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8AVX512F (Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_Str8FindResult Dqn_Str8_FindLastStr8AVX512F (Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitAVX512F (Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastAVX512F(Dqn_Str8 string, Dqn_Str8 find);
DQN_API Dqn_usize Dqn_Str8_SplitAVX512F (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAllocAVX512F (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
#endif // DQN_AVX512F_H

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -21,16 +24,196 @@
#include <cpuid.h>
#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)

View File

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

View File

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

View File

@ -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,

View File

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

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -281,6 +284,8 @@ template <typename T, Dqn_usize N> void Dqn_FArra
#define DQN_TO_SLICE(val) Dqn_Slice_Init((val)->data, (val)->size)
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--) {
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -249,222 +252,6 @@ DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, b
}
#endif // !defined(DQN_NO_JSON_BUILDER)
#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;
}

View File

@ -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
View File

@ -0,0 +1,429 @@
#pragma once
#include "dqn.h"
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
{
void *result = NULL;
if (!user_data)
return result;
Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data;
result = Dqn_Arena_Alloc(arena, count, alignof(json_value_s), Dqn_ZeroMem_No);
return result;
}
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size)
{
switch (type) {
case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; }
case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; }
case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; }
case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; }
case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; }
case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; }
default: /*FALLTHRU*/
case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; }
}
}
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_Str8 key)
{
bool result = false;
if (lhs && Dqn_Str8_HasData(key)) {
Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size);
result = Dqn_Str8_Eq(lhs_string, key);
}
return result;
}
// NOTE: Dqn_JSON_It ///////////////////////////////////////////////////////////////////////////////
Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json)
{
json_parse_result_s parse_result = {};
json_value_ex_s *ex_value =
DQN_CAST(json_value_ex_s *) json_parse_ex(json.data,
json.size,
json_parse_flags_allow_location_information,
Dqn_JSON_ArenaAllocFunc,
arena,
&parse_result);
Dqn_JSONIt result = {};
Dqn_JSON_ItPushValue(&result, &ex_value->value);
return result;
}
// NOTE: Dqn_JSON_ItPush/Pop ///////////////////////////////////////////////////////////////////////
bool Dqn_JSON_ItPushObjElement(Dqn_JSONIt *it, json_object_element_s *element)
{
if (!it || !element)
return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObjElement, element};
return true;
}
bool Dqn_JSON_ItPushObj(Dqn_JSONIt *it, json_object_s *obj)
{
if (!it || !obj)
return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObj, obj};
return true;
}
bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element)
{
if (!it || !element)
return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArrayElement, element};
return true;
}
bool Dqn_JSON_ItPushArray(Dqn_JSONIt *it, json_value_s *value)
{
if (!it || !value || json_value_as_array(value) == nullptr)
return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArray, value};
return true;
}
bool Dqn_JSON_ItPushValue(Dqn_JSONIt *it, json_value_s *value)
{
bool result = false;
if (!it || !value)
return result;
if (value->type == json_type_object) {
result = Dqn_JSON_ItPushObj(it, json_value_as_object(value));
} else if (value->type == json_type_array) {
result = Dqn_JSON_ItPushArray(it, value);
}
return result;
}
void Dqn_JSON_ItPop(Dqn_JSONIt *it)
{
if (!it)
return;
DQN_ASSERT(it->stack_count > 0);
if (it->stack_count > 0)
it->stack_count--;
}
// NOTE: Dqn_JSON_It JSON tree navigation //////////////////////////////////////////////////////////
json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it)
{
json_value_s *result = nullptr;
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
if (!curr)
return result;
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value;
result = element->value;
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value;
result = element->value;
} else {
result = DQN_CAST(json_value_s *) curr->value;
}
if (result->type == json_type_array) {
json_array_s *array = json_value_as_array(result);
DQN_ASSERT(array);
Dqn_JSON_ItPushArray(it, result);
} else if (result->type == json_type_object) {
json_object_s *obj = json_value_as_object(result);
DQN_ASSERT(obj);
Dqn_JSON_ItPushObj(it, obj);
}
return result;
}
bool Dqn_JSON_ItNext(Dqn_JSONIt *it)
{
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
if (!curr)
return false;
json_object_element_s *obj_element = nullptr;
json_array_element_s *array_element = nullptr;
if (curr->type == Dqn_JSON_ItEntryTypeObj) {
auto *obj = DQN_CAST(json_object_s *) curr->value;
obj_element = obj->start;
} else if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
auto *element = DQN_CAST(json_object_element_s *) curr->value;
obj_element = element->next;
Dqn_JSON_ItPop(it);
} else if (curr->type == Dqn_JSON_ItEntryTypeArray) {
auto *value = DQN_CAST(json_value_s *) curr->value;
auto *array = json_value_as_array(value);
array_element = array->start;
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
auto *element = DQN_CAST(json_array_element_s *) curr->value;
array_element = element->next;
Dqn_JSON_ItPop(it);
} else {
Dqn_JSON_ItPop(it);
}
if (obj_element)
Dqn_JSON_ItPushObjElement(it, obj_element);
else if (array_element)
Dqn_JSON_ItPushArrayElement(it, array_element);
bool result = obj_element || array_element;
return result;
}
// NOTE: Dqn_JSON_ItCurr ///////////////////////////////////////////////////////////////////////////
Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it)
{
Dqn_JSONItEntry *result = nullptr;
if (!it || it->stack_count <= 0)
return result;
result = &it->stack[it->stack_count - 1];
return result;
}
json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it)
{
json_value_s *result = nullptr;
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
if (!curr)
return result;
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
auto *element = DQN_CAST(json_object_element_s *)curr->value;
result = element->value;
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
auto *element = DQN_CAST(json_array_element_s *)curr->value;
result = element->value;
} else if (curr->type == Dqn_JSON_ItEntryTypeString ||
curr->type == Dqn_JSON_ItEntryTypeNumber ||
curr->type == Dqn_JSON_ItEntryTypeObj ||
curr->type == Dqn_JSON_ItEntryTypeArray)
{
result = DQN_CAST(json_value_s *)curr->value;
}
return result;
}
json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it)
{
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement)
? DQN_CAST(json_object_element_s *) curr->value
: nullptr;
return result;
}
// NOTE: Dqn_JSON_ItValueIs ////////////////////////////////////////////////////////////////////////
json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
return result;
}
json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
return result;
}
json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
return result;
}
json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
return result;
}
json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_number_s *result = curr ? json_value_as_number(curr) : nullptr;
return result;
}
json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr;
return result;
}
json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it)
{
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr;
return result;
}
size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it)
{
size_t result = 0;
if (json_array_s *curr = Dqn_JSON_ItValueIsArray(it))
result = curr->length;
return result;
}
// NOTE: Dqn_JSON_ItKeyValueIs /////////////////////////////////////////////////////////////////////
Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
Dqn_Str8 result = {};
if (curr) {
result.data = DQN_CAST(char *)curr->name->string;
result.size = curr->name->string_size;
}
return result;
}
bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
bool result = Dqn_JSON_String8Cmp(curr->name, key);
return result;
}
json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_s *result = nullptr;
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_object(curr->value);
return result;
}
json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_array_s *result = nullptr;
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_array(curr->value);
return result;
}
json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
json_string_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_string(curr->value);
return result;
}
json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
json_number_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_number(curr->value);
return result;
}
json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
json_value_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr;
return result;
}
json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key)
{
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
json_value_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_null ? curr->value : nullptr;
return result;
}
// NOTE: Dqn_JSON_ItValueTo ////////////////////////////////////////////////////////////////////////
Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it)
{
Dqn_Str8 result = {};
if (json_string_s *curr = Dqn_JSON_ItValueIsString(it))
result = Dqn_Str8_Init(curr->string, curr->string_size);
return result;
}
int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it)
{
int64_t result = {};
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
return result;
}
uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it)
{
uint64_t result = {};
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
return result;
}
bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it)
{
bool result = {};
if (json_value_s *curr = Dqn_JSON_ItValueIsBool(it))
result = curr->type == json_type_true;
return result;
}
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site)
{
if (!it)
return;
json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it);
if (!curr)
return;
size_t value_type_size = 0;
char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)curr->value->type, &value_type_size);
json_string_s const *key = curr->name;
if (it->flags & json_parse_flags_allow_location_information) {
json_string_ex_s const *info = DQN_CAST(json_string_ex_s const *)key;
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
call_site,
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no,
info->row_no,
DQN_CAST(int)key->string_size,
key->string,
DQN_CAST(int)value_type_size,
value_type);
} else {
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
call_site,
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
DQN_CAST(int)key->string_size,
key->string,
DQN_CAST(int)value_type_size,
value_type);
}
}

View File

@ -1,9 +1,10 @@
#pragma once
#include "dqn.h"
#if !defined(SHEREDOM_JSON_H_INCLUDED)
#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)

View File

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

View File

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

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -75,7 +78,7 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_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;
}

View File

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

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -137,6 +140,20 @@ DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date)
return result;
}
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)

View File

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

View File

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

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -156,7 +159,7 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8
return result;
}
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;
}

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -159,14 +162,14 @@ DQN_API Dqn_Str8 Dqn_Str8_Advance
DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string);
DQN_API Dqn_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);

View File

@ -1,3 +1,6 @@
#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -58,6 +61,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
// 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,21 +9,8 @@ separated by category into files for organisation. You only need to include
## Build
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>`.