diff --git a/dqn.h b/dqn.h index 37fb0c3..b8b7f93 100644 --- a/dqn.h +++ b/dqn.h @@ -1,55 +1,54 @@ /// @file dqn.h #if !defined(DQN_H) #define DQN_H -// ---------------------------------+-----------------------------+--------------------------------- -// Table Of Contents | Compile out with #define | Description -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CFGM] Config macros | | Compile time customisation of library -// [SECT-CMAC] Compiler macros | | Macros for the compiler -// [SECT-INCL] Include files | | Standard library Include dependencies -// [SECT-MACR] Macros | | Define macros used in the library -// [SECT-TYPE] Typedefs | | Typedefs used in the library -// [SECT-GSTR] Global Structs | | Forward declare useful structs -// [SECT-W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset -// [SECT-INTR] Intrinsics | | Atomics, cpuid, ticket mutex -// [SECT-CARR] Dqn_CArray | | Compile time C-array size -// [SECT-STBS] stb_sprintf | | Portable sprintf -// [SECT-CALL] Dqn_CallSite | | Source code location/tracing -// [SECT-ALLO] Dqn_Allocator | | Generic allocator interface -// [SECT-CSTR] Dqn_CString8 | | C-string helpers -// [SECT-STR8] Dqn_String8 | | Pointer and length strings -// [SECT-PRIN] Dqn_Print | | Console printing -// [SECT-LLOG] Dqn_Log | | Library logging -// [SECT-VMEM] Dqn_VMem | | Virtual memory allocation -// [SECT-AREN] Dqn_Arena | | Growing bump allocator -// [SECT-VARR] Dqn_VArray | DQN_NO_VARRAY | Array backed by virtual memory arena -// [SECT-DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair -// [SECT-DLIB] Dqn_Library | | Library run-time behaviour configuration -// [SECT-FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings -// [SECT-STRB] Dqn_String8Builder | | -// [SECT-FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays -// [SECT-LIST] Dqn_List | | Chunked linked lists, append only -// [SECT-MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp -// [SECT-BITS] Dqn_Bit | | Bitset manipulation -// [SECT-SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts -// [SECT-CHAR] Dqn_Char | | Character ascii/digit.. helpers -// [SECT-UTFX] Dqn_UTF | | Unicode helpers -// [SECT-BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers -// [SECT-DATE] Dqn_Date | | Date-time helpers -// [SECT-WIND] Dqn_Win | | Windows OS helpers -// [SECT-WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers -// [SECT-OSYS] Dqn_OS | | Operating-system APIs -// [SECT-FSYS] Dqn_Fs | | Filesystem helpers -// [SECT-MISC] Miscellaneous | | General purpose helpers -// [SECT-TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas -// [SECT-JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output -// [SECT-FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a -// [SECT-MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// Table Of Contents | Compile out with #define | Description +// ================================================================================================= +// [$CFGM] Config macros | | Compile time customisation of library +// [$CMAC] Compiler macros | | Macros for the compiler +// [$INCL] Include files | | Standard library Include dependencies +// [$MACR] Macros | | Define macros used in the library +// [$TYPE] Typedefs | | Typedefs used in the library +// [$GSTR] Global Structs | | Forward declare useful structs +// [$W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset +// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex +// [$STBS] stb_sprintf | | Portable sprintf +// [$CALL] Dqn_CallSite | | Source code location/tracing +// [$ALLO] Dqn_Allocator | | Generic allocator interface +// [$CSTR] Dqn_CString8 | | C-string helpers +// [$STR8] Dqn_String8 | | Pointer and length strings +// [$PRIN] Dqn_Print | | Console printing +// [$LLOG] Dqn_Log | | Library logging +// [$VMEM] Dqn_VMem | | Virtual memory allocation +// [$AREN] Dqn_Arena | | Growing bump allocator +// [$VARR] Dqn_VArray | DQN_NO_VARRAY | Array backed by virtual memory arena +// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair +// [$DLIB] Dqn_Library | | Library run-time behaviour configuration +// [$FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings +// [$STRB] Dqn_String8Builder | | +// [$FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays +// [$LIST] Dqn_List | | Chunked linked lists, append only +// [$MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp +// [$BITS] Dqn_Bit | | Bitset manipulation +// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts +// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers +// [$UTFX] Dqn_UTF | | Unicode helpers +// [$BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers +// [$DATE] Dqn_Date | | Date-time helpers +// [$WIND] Dqn_Win | | Windows OS helpers +// [$WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers +// [$OSYS] Dqn_OS | | Operating-system APIs +// [$FSYS] Dqn_Fs | | Filesystem helpers +// [$MISC] Miscellaneous | | General purpose helpers +// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas +// [$JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output +// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a +// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 +// ================================================================================================= // -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CFGM] Config macros | | Compile time customisation of library -// ------------------------------------------------------------------------------------------------- +// ================================================================================================= +// [$CFGM] Config macros | | Compile time customisation of library +// ================================================================================================= // #define DQN_IMPLEMENTATION // Define this in one and only one C++ file to enable the implementation // code of the header file @@ -91,9 +90,9 @@ // thread context. The thread context arena stats can be printed by using // Dqn_Library_DumpThreadContextArenaStat. // -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CMAC] Compiler macros | | Macros for the compiler -// ------------------------------------------------------------------------------------------------- +// ================================================================================================= +// [$CMAC] Compiler macros | | Macros for the compiler +// ================================================================================================= // NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER #if defined(_MSC_VER) #if defined(__clang__) @@ -123,17 +122,17 @@ #endif #endif -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-INCL] Include files | | Standard library Include dependencies -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$INCL] Include files | | Standard library Include dependencies +// ================================================================================================= #include // va_list #include // fprintf, FILE, stdout, stderr #include // [u]int_*, ... #include // [U]INT_MAX, ... -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MACR] Macros | | Define macros used in the library -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MACR] Macros | | Define macros used in the library +// ================================================================================================= #define Dqn_PowerOfTwoRoundUp(value, power_of_two) (((value) + ((power_of_two) - 1)) & ~((power_of_two) - 1)) // NOTE: Memory allocation dependencies @@ -336,9 +335,9 @@ struct Dqn_DeferHelper DQN_UNIQUE_NAME(once); \ end, DQN_UNIQUE_NAME(once) = false) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-TYPE] Typedefs | | Typedefs used in the library -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$TYPE] Typedefs | | Typedefs used in the library +// ================================================================================================= typedef intptr_t Dqn_isize; typedef uintptr_t Dqn_usize; typedef intptr_t Dqn_isize; @@ -351,9 +350,9 @@ typedef int32_t Dqn_b32; #define DQN_ISIZE_MAX INTPTR_MAX #define DQN_ISIZE_MIN INTPTR_MIN -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-GSTR] Global Structs | | Forward declare useful structs -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$GSTR] Global Structs | | Forward declare useful structs +// ================================================================================================= struct Dqn_String8 ///< Pointer and length style UTF8 strings { char *data; ///< The UTF8 bytes of the string @@ -367,9 +366,9 @@ struct Dqn_String8 ///< Pointer and length style UTF8 strings #endif }; -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset +// ================================================================================================= #if defined(DQN_OS_WIN32) #if !defined(DQN_NO_WIN32_MINIMAL_HEADER) && !defined(_INC_WINDOWS) // Taken from Windows.h @@ -395,9 +394,9 @@ struct Dqn_String8 ///< Pointer and length style UTF8 strings #endif // !defined(DQN_NO_WIN32_MINIMAL_HEADER) && !defined(_INC_WINDOWS) #endif // !defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-INTR] Intrinsics | | Atomics, cpuid, ticket mutex -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex +// ================================================================================================= typedef enum Dqn_ZeroMem { Dqn_ZeroMem_No, ///< Memory can be handed out without zero-ing it out Dqn_ZeroMem_Yes, ///< Memory should be zero-ed out before giving to the callee @@ -406,15 +405,15 @@ typedef enum Dqn_ZeroMem { // NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target #if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #include - #define Dqn_Atomic_AddU32(target, value) _InterlockedExchangeAdd(DQN_CAST(long volatile *)target, value) - #define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64(DQN_CAST(__int64 volatile *)target, value) - #define Dqn_Atomic_SubU32(target, value) DQN_CAST(unsigned)Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, -DQN_CAST(long)value) - #define Dqn_Atomic_SubU64(target, value) DQN_CAST(uint64_t)Dqn_Atomic_AddU64(target, -DQN_CAST(uint64_t)value) - #define Dqn_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64(DQN_CAST(__int64 volatile *)dest, desired_val, prev_val) - #define Dqn_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange(DQN_CAST(long volatile *)dest, desired_val, prev_val) - #define Dqn_CPUClockCycle() __rdtsc() - #define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence() - #define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence() + #define Dqn_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val) + #define Dqn_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val) + #define Dqn_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value) + #define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value) + #define Dqn_Atomic_SubU32(target, value) Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, (long)-value) + #define Dqn_Atomic_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value) + #define Dqn_CPUClockCycle() __rdtsc() + #define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence() + #define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence() #elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) #include #define Dqn_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) @@ -426,7 +425,7 @@ typedef enum Dqn_ZeroMem { #else #define Dqn_CPUClockCycle() __builtin_readcyclecounter() #endif - #define Dqn_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory") + #define Dqn_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory") #define Dqn_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory") #else #error "Compiler not supported" @@ -525,19 +524,9 @@ void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket); /// Determine if the mutex can be locked using the given ticket number. bool Dqn_TicketMutex_CanLock(Dqn_TicketMutex const *mutex, Dqn_uint ticket); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CARR] Dqn_CArray | | Compile time C-array size -// ---------------------------------+-----------------------------+--------------------------------- -/// Calculate the size of a C-array at compile time -/// @param array The C-array to calculate the size for -/// @return The size of the c-array buffer -template constexpr Dqn_usize Dqn_CArray_Count (T const (&array)[N]) { (void)array; return N; } -template constexpr Dqn_isize Dqn_CArray_CountI (T const (&array)[N]) { (void)array; return N; } -template constexpr Dqn_isize Dqn_CArray_CountInt(T const (&array)[N]) { (void)array; return N; } - -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-STBS] stb_sprintf | | Portable sprintf -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$STBS] stb_sprintf | | Portable sprintf +// ================================================================================================= /// @cond NO_DOXYYGEN // stb_sprintf - v1.10 - public domain snprintf() implementation // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 @@ -757,9 +746,9 @@ STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri #endif // STB_SPRINTF_H_INCLUDE /// @endcond -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CALL] Dqn_CallSite | | Source code location/tracing -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$CALL] Dqn_CallSite | | Source code location/tracing +// ================================================================================================= typedef struct Dqn_CallSite { Dqn_String8 file; Dqn_String8 function; @@ -768,9 +757,9 @@ typedef struct Dqn_CallSite { #define DQN_CALL_SITE Dqn_CallSite{DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__} -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-ALLO] Dqn_Allocator | | Generic allocator interface -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$ALLO] Dqn_Allocator | | Generic allocator interface +// ================================================================================================= #if defined(DQN_LEAK_TRACING) #if defined(DQN_NO_DSMAP) #error "DSMap is required for allocation tracing" @@ -840,9 +829,9 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi #define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type) * count, alignof(Type), zero_mem) #define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type), alignof(Type), zero_mem) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CSTR] Dqn_CString8 | | C-string helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$CSTR] Dqn_CString8 | | C-string helpers +// ================================================================================================= /// Calculate the size of a cstring literal/array at compile time /// @param literal The cstring literal/array to calculate the size for /// @return The size of the cstring not including the null-terminating byte @@ -1062,9 +1051,9 @@ DQN_API bool Dqn_CString8_IsAllDigits(char const *src, Dqn_isize size); DQN_API Dqn_isize Dqn_CString16_Size(wchar_t const *a); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-STR8] Dqn_String8 | | Pointer and length strings -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$STR8] Dqn_String8 | | Pointer and length strings +// ================================================================================================= /// Construct a UTF8 c-string literal into a Dqn_String8 referencing a string /// stored in the data-segment. This string is read-only. #define DQN_STRING8(string) Dqn_String8{(char *)(string), sizeof(string) - 1} @@ -1243,9 +1232,9 @@ bool operator==(Dqn_String8 const &lhs, Dqn_String8 const &rhs); bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs); #endif -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-PRIN] Dqn_Print | | Console printing -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$PRIN] Dqn_Print | | Console printing +// ================================================================================================= enum Dqn_PrintStd { Dqn_PrintStd_Out, @@ -1268,9 +1257,9 @@ DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...); #define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args) #define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-LLOG] Dqn_Log | | Library logging -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$LLOG] Dqn_Log | | Library logging +// ================================================================================================= enum Dqn_LogType { Dqn_LogType_Error, @@ -1308,7 +1297,7 @@ DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char c DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...); // ================================================================================================= -// [SECT-VMEM] Dqn_VMem | | Virtual memory allocation +// [$VMEM] Dqn_VMem | | Virtual memory allocation // ================================================================================================= enum Dqn_VMemCommit { Dqn_VMemCommit_No, @@ -1321,7 +1310,7 @@ DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size); DQN_API void Dqn_VMem_Release(void *ptr, Dqn_usize size); // ================================================================================================= -// [SECT-AREN] Dqn_Arena | | Growing bump allocator +// [$AREN] Dqn_Arena | | Growing bump allocator // ================================================================================================= // // A bump-allocator that can grow dynamically by chaining blocks of memory @@ -1498,9 +1487,34 @@ DQN_API void Dqn_Arena_Free_ (DQN_LEAK_TRACE_FUNCTION DQN_API void * Dqn_Arena_CopyZ_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_isize size, uint8_t alignment); DQN_API void Dqn_Arena_EndTempMemory_ (DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemory arena_temp_memory); +// ================================================================================================= +// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog +// ================================================================================================= +struct Dqn_ArenaCatalogItem +{ + Dqn_Arena *arena; + Dqn_String8 label; + Dqn_ArenaCatalogItem *next; + Dqn_ArenaCatalogItem *prev; +}; + +struct Dqn_ArenaCatalog +{ + Dqn_TicketMutex ticket_mutex; ///< Mutex for adding to the linked list of arenas + Dqn_Arena *arena; + Dqn_ArenaCatalogItem sentinel; + uint16_t arena_count; +}; + +DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena); +DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, char const *fmt, va_list args); +DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, char const *fmt, ...); +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args); +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...); + #if !defined(DQN_NO_VARRAY) // ================================================================================================= -// [SECT-VARR] Dqn_VArray | DQN_NO_VARRAY | Array backed by virtual memory arena +// [$VARR] Dqn_VArray | DQN_NO_VARRAY | Array backed by virtual memory arena // ================================================================================================= template struct Dqn_VArray { @@ -1526,7 +1540,7 @@ DQN_API template void Dqn_VArray_EraseRange (Dqn_VArray< #if !defined(DQN_NO_DSMAP) // ================================================================================================= -// [SECT-DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair +// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair // ================================================================================================= // // A hash table configured using the presets recommended by Demitri Spanos @@ -1728,59 +1742,79 @@ DQN_API bool operator== (Dqn_DS #endif // !defined(DQN_NO_DSMAP) // ================================================================================================= -// [SECT-DLIB] Dqn_Library | | Library run-time behaviour configuration +// [$DLIB] Dqn_Library | | Library run-time behaviour configuration // ================================================================================================= +// +// Book-keeping data for the library and allow customisation of certain features +// provided. +// +// NOTE: API +// +// @proc Dqn_Library_SetLogCallback +// @desc Update the default logging function, all logging functions will run through +// this callback +// @param[in] proc The new logging function, set to nullptr to revert back to +// the default logger. +// @param[in] user_data A user defined parameter to pass to the callback +// +// @proc Dqn_Library_SetLogFile +// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise +// point it to the FILE that you wish to write to. +// +// @proc Dqn_Library_DumpThreadContextArenaStat +// @desc Dump the per-thread arena statistics to the specified file + struct Dqn_Library { - Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine - void * log_user_data; - bool log_to_file; ///< Output logs to file as well as standard out - void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ? - Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time + bool lib_init; + Dqn_TicketMutex lib_mutex; -#if defined(DQN_LEAK_TRACING) + Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine + void * log_user_data; + bool log_to_file; ///< Output logs to file as well as standard out + void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ? + Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time + + /// The backup arena to use if no arena is passed into Dqn_Library_Init + Dqn_Arena arena_catalog_backup_arena; + Dqn_ArenaCatalog arena_catalog; + + #if defined(DQN_LEAK_TRACING) Dqn_TicketMutex alloc_table_mutex; Dqn_DSMap alloc_table; -#endif + #endif -#if defined(DQN_OS_WIN32) - LARGE_INTEGER win32_qpc_frequency; - Dqn_TicketMutex win32_bcrypt_rng_mutex; - void *win32_bcrypt_rng_handle; -#endif + #if defined(DQN_OS_WIN32) + LARGE_INTEGER win32_qpc_frequency; + Dqn_TicketMutex win32_bcrypt_rng_mutex; + void *win32_bcrypt_rng_handle; + #endif -#if defined(DQN_DEBUG_THREAD_CONTEXT) - Dqn_TicketMutex thread_context_mutex; - Dqn_ArenaStat thread_context_arena_stats[256]; - uint8_t thread_context_arena_stats_count; -#endif + #if defined(DQN_DEBUG_THREAD_CONTEXT) + Dqn_TicketMutex thread_context_mutex; + Dqn_ArenaStat thread_context_arena_stats[256]; + uint8_t thread_context_arena_stats_count; + #endif } extern dqn_library; -/// Update the default logging function, all logging functions will run through -/// this callback -/// @param[in] proc The new logging function, set to nullptr to revert back to -/// the default logger. -/// @param[in] user_data A user defined parameter to pass to the callback -DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data); - -/// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise -/// point it to the FILE that you wish to write to. -DQN_API void Dqn_Library_SetLogFile(void *file); - -/// Dump the per-thread arena statistics to the specified file -DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path); +// NOTE: Properties ================================================================================ +DQN_API Dqn_Library *Dqn_Library_Init (Dqn_Arena *arena); +DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data); +DQN_API void Dqn_Library_SetLogFile (void *file); +DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path); +// NOTE: Leak Trace ================================================================================ #if defined(DQN_LEAK_TRACING) -void Dqn_Library_LeakTraceAdd(Dqn_CallSite call_site, void *ptr, Dqn_usize size); -void Dqn_Library_LeakTraceMarkFree(Dqn_CallSite call_site, void *ptr); +DQN_API void Dqn_Library_LeakTraceAdd (Dqn_CallSite call_site, void *ptr, Dqn_usize size); +DQN_API void Dqn_Library_LeakTraceMarkFree (Dqn_CallSite call_site, void *ptr); #else -#define Dqn_Library_LeakTraceAdd(...) -#define Dqn_Library_LeakTraceMarkFree(...) +#define Dqn_Library_LeakTraceAdd(...) +#define Dqn_Library_LeakTraceMarkFree(...) #endif -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings +// ================================================================================================= #if !defined(DQN_NO_FSTRING8) #define DQN_FSTRING8_API template DQN_API #define DQN_FSTRING8 Dqn_FString8 @@ -1871,9 +1905,9 @@ template bool Dqn_FString8_EqABInsensitive(Dqn_FString8 const *lhs, Dqn_FString8 const *rhs); #endif // !defined(DQN_NO_FSTRING8) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-STRB] Dqn_String8Builder | | -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$STRB] Dqn_String8Builder | | +// ================================================================================================= struct Dqn_String8Builder { Dqn_Allocator allocator; ///< Allocator to use to back the string list @@ -1919,9 +1953,9 @@ bool Dqn_String8Builder_AppendF(Dqn_String8Builder *builder, char const *fmt, .. /// invalid or memory allocation failure. Dqn_String8 Dqn_String8Builder_Build(Dqn_String8Builder const *builder, Dqn_Allocator allocator); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays +// ================================================================================================= #if !defined(DQN_NO_FARRAY) #define DQN_FARRAY_API template DQN_API #define DQN_FARRAY Dqn_FArray @@ -2028,9 +2062,9 @@ DQN_API T *Dqn_FArray_FindProcElseMake(DQN_FARRAY *array, IsEqualProc IsEqual, b DQN_FARRAY_API T *Dqn_FArray_Find(DQN_FARRAY *array, T const *find); #endif // !defined(DQN_NO_FARRAY) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-LIST] Dqn_List | | Chunked linked lists, append only -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$LIST] Dqn_List | | Chunked linked lists, append only +// ================================================================================================= template struct Dqn_ListChunk { @@ -2092,9 +2126,9 @@ T *Dqn_List_At(Dqn_List *list, Dqn_isize index, Dqn_ListChunk *at_chunk); template DQN_API bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_isize start_index); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp +// ================================================================================================= #if !defined(DQN_NO_MATH) struct Dqn_V2I { @@ -2307,17 +2341,17 @@ DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); #endif // !defined(DQN_NO_MATH) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-BITS] Dqn_Bit | | Bitset manipulation -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$BITS] Dqn_Bit | | Bitset manipulation +// ================================================================================================= DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield); DQN_API void Dqn_Bit_SetInplace(uint32_t *flags, uint32_t bitfield); DQN_API bool Dqn_Bit_IsSet(uint32_t bits, uint32_t bits_to_set); DQN_API bool Dqn_Bit_IsNotSet(uint32_t bits, uint32_t bits_to_check); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts +// ================================================================================================= #if defined(NDEBUG) #define Dqn_Safe_AssertF(expr, fmt, ...) #define Dqn_Safe_Assert(expr, fmt, ...) @@ -2581,9 +2615,9 @@ DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val); /// out of the valid range when casted. DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CHAR] Dqn_Char | | Character ascii/digit.. helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers +// ================================================================================================= DQN_API bool Dqn_Char_IsAlpha (char ch); DQN_API bool Dqn_Char_IsDigit (char ch); DQN_API bool Dqn_Char_IsAlphaNum (char ch); @@ -2594,15 +2628,15 @@ DQN_API char Dqn_Char_ToHex (char ch); DQN_API char Dqn_Char_ToHexUnchecked(char ch); DQN_API char Dqn_Char_ToLower (char ch); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-UTFX] Dqn_UTF | | Unicode helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$UTFX] Dqn_UTF | | Unicode helpers +// ================================================================================================= DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint); DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers +// ================================================================================================= #if !defined(DQN_NO_HEX) DQN_API char const *Dqn_Hex_TrimSpaceAnd0xPrefixCString8(char const *hex, Dqn_isize size, Dqn_isize *real_size); DQN_API Dqn_String8 Dqn_Hex_TrimSpaceAnd0xPrefixString8(Dqn_String8 const string); @@ -2715,9 +2749,9 @@ DQN_API char *Dqn_Hex_CString8ToBytesArena(Dqn_Arena *arena, char const *hex, Dq DQN_API Dqn_String8 Dqn_Hex_String8ToBytesArena(Dqn_Arena *arena, Dqn_String8 hex); #endif // !defined(DQN_NO_HEX) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-DATE] Dqn_Date | | Date-time helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$DATE] Dqn_Date | | Date-time helpers +// ================================================================================================= struct Dqn_DateHMSTimeString { char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")]; @@ -2747,9 +2781,9 @@ DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString(Dqn_DateHMSTime time, DQN_API uint64_t Dqn_Date_EpochTime(); #if defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-WIND] Dqn_Win | | Windows OS helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$WIND] Dqn_Win | | Windows OS helpers +// ================================================================================================= struct Dqn_WinErrorMsg { unsigned long code; @@ -2834,9 +2868,9 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it); #if !defined(DQN_NO_WINNET) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers +// ================================================================================================= enum Dqn_WinNetHandleState { Dqn_WinNetHandleState_Invalid, @@ -2945,9 +2979,9 @@ DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString(Dqn_WinNetHandle *ha #endif // !defined(DQN_NO_WINNET) #endif // defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs +// ================================================================================================= /// Generate cryptographically secure bytes DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size); @@ -3034,9 +3068,9 @@ struct Dqn_OSTimedBlock DQN_LOG_D("%s -> %s (total): %fms", t1.label, t2.label, Dqn_OS_PerfCounterMs(t1.tick, t2.tick));\ } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FSYS] Dqn_Fs | | Filesystem helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FSYS] Dqn_Fs | | Filesystem helpers +// ================================================================================================= enum Dqn_FsInfoType { Dqn_FsInfoType_Unknown, @@ -3121,9 +3155,9 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_isize size); DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MISC] Miscellaneous | | General purpose helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MISC] Miscellaneous | | General purpose helpers +// ================================================================================================= /// Write the format string to the buffer truncating with a trailing ".." if /// there is insufficient space in the buffer followed by null-terminating the /// buffer (uses stb_sprintf underneath). @@ -3165,12 +3199,38 @@ Dqn_BinarySearch(T const *array, Dqn_BinarySearchType type = Dqn_BinarySearchType_Match, Dqn_BinarySearchLessThanProc less_than = Dqn_BinarySearch_DefaultLessThan); -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas -// ---------------------------------+-----------------------------+--------------------------------- -// Utility functions for building applications by providing an in-built thread -// context that gives the user access to a temporary arena automatically without -// explicit setup on the end application's side. +// ================================================================================================= +// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas +// ================================================================================================= +// +// Each thread is assigned in their thread-local storage (TLS) scratch and +// permanent arena allocators. These can be used for allocations with a lifetime +// scoped to the lexical scope or for storing data permanently using the arena +// paradigm. +// +// TLS in this implementation is implemented using the `thread_local` C/C++ +// keyword. +// +// NOTE: API +// +// @proc Dqn_Thread_GetContext +// @desc Get the current thread's context- this contains all the metadata for managing +// the thread scratch data. In general you probably want Dqn_Thread_GetScratch() +// which ensures you get a usable scratch arena for temporary allocations +// without having to worry about selecting the right arena from the state. +// +// @proc Dqn_Thread_GetScratch +// @desc Retrieve the per-thread temporary arena allocator that is reset on scope +// exit. +// +// The scratch arena must be deconflicted with any existing arenas in the +// function to avoid trampling over each other's memory. Consider the situation +// where the scratch arena is passed into the function. Inside the function, if +// the same arena is reused then, if both arenas allocate, when the inner arena +// is reset, this will undo the passed in arena's allocations in the function. +// +// @param[in] conflict_arena A pointer to the arena currently being used in the +// function #if !defined(DQN_THREAD_CONTEXT_ARENAS) #define DQN_THREAD_CONTEXT_ARENAS 2 @@ -3178,20 +3238,20 @@ Dqn_BinarySearch(T const *array, struct Dqn_ThreadContext { - Dqn_b32 init; + Dqn_b32 init; - Dqn_Arena arena; ///< Per thread arena - Dqn_Allocator allocator; ///< Allocator that uses the arena + Dqn_Arena *arena; ///< Per thread arena + Dqn_Allocator allocator; ///< Allocator that uses the arena /// Temp memory arena's for the calling thread - Dqn_Arena temp_arenas[DQN_THREAD_CONTEXT_ARENAS]; + Dqn_Arena *temp_arenas[DQN_THREAD_CONTEXT_ARENAS]; /// Allocators that use the corresponding arena from the thread context. /// Provided for convenience when interfacing with allocator interfaces. - Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS]; + Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS]; #if defined(DQN_DEBUG_THREAD_CONTEXT) - Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS]; + Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS]; #endif }; @@ -3212,30 +3272,18 @@ struct Dqn_ThreadScratch #endif }; -// Get the current thread's context- this contains all the metadata for managing -// the thread scratch data. In general you probably want Dqn_Thread_GetScratch() -// which ensures you get a usable scratch arena for temporary allocations -// without having to worry about selecting the right arena from the state. -#define Dqn_Thread_GetContext() Dqn_Thread_GetContext_(DQN_LEAK_TRACE_NO_COMMA) +// NOTE: Context =================================================================================== +#define Dqn_Thread_GetContext() Dqn_Thread_GetContext_(DQN_LEAK_TRACE_NO_COMMA) +#define Dqn_Thread_GetScratch(conflict_arena) Dqn_Thread_GetScratch_(DQN_LEAK_TRACE conflict_arena) +DQN_API uint32_t Dqn_Thread_GetID(); + +// NOTE: Internal ================================================================================== DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA); +DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena); -/// Retrieve the per-thread temporary arena allocator that is reset on scope -/// exit. -/// -/// The scratch arena must be deconflicted with any existing arenas in the -/// function to avoid trampling over each other's memory. Consider the situation -/// where the scratch arena is passed into the function. Inside the function, if -/// the same arena is reused then, if both arenas allocate, when the inner arena -/// is reset, this will undo the passed in arena's allocations in the function. -/// -/// @param[in] conflict_arena A pointer to the arena currently being used in the -/// function -#define Dqn_Thread_GetScratch(conflict_arena) Dqn_Thread_GetScratch_(DQN_LEAK_TRACE conflict_arena) -DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena); - -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output +// ================================================================================================= #if !defined(DQN_NO_JSON_BUILDER) // TODO(dqn): We need to write tests for this enum Dqn_JSONBuilderItem { @@ -3349,9 +3397,9 @@ void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, boo #define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STRING8(""), value) #endif // !defined(DQN_NO_JSON_BUIDLER) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a +// ================================================================================================= #ifndef DQN_FNV1A32_SEED #define DQN_FNV1A32_SEED 2166136261U #endif @@ -3372,7 +3420,7 @@ DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_isize size, uint32_t DQN_API uint64_t Dqn_FNV1A64_Iterate(void const *bytes, Dqn_isize size, uint64_t hash); // ================================================================================================= -// [SECT-MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 +// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 // ================================================================================================= // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author (Austin Appleby) hereby disclaims copyright to this source @@ -3391,7 +3439,7 @@ DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32 #if !defined(DQN_NO_VARRAY) // ================================================================================================= -// [SECT-VARR] Dqn_VArray | | Array backed by virtual memory arena +// [$VARR] Dqn_VArray | | Array backed by virtual memory arena // ================================================================================================= DQN_API template Dqn_VArray Dqn_VArray_InitByteSize(Dqn_Arena *arena, Dqn_usize byte_size) { @@ -3487,7 +3535,7 @@ DQN_API template void Dqn_VArray_EraseRange(Dqn_VArray *array, D #if !defined(DQN_NO_DSMAP) // ================================================================================================= -// [SECT-DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair +// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair // ================================================================================================= uint32_t const DQN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; uint32_t const DQN_DS_MAP_SENTINEL_SLOT = 0; @@ -3840,9 +3888,9 @@ DQN_API Dqn_DSMapKey Dqn_DSMap_KeyString8Copy(Dqn_DSMap const *map, Dqn_Alloc #endif // !defined(DQN_NO_DSMAP) #if !defined(DQN_NO_FSTRING8) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings +// ================================================================================================= DQN_FSTRING8_API DQN_FSTRING8 Dqn_FString8_InitF(char const *fmt, ...) { @@ -3983,9 +4031,9 @@ bool Dqn_FString8_EqABInsensitive(Dqn_FString8 const *lhs, Dqn_FString8 co #endif // !defined(DQN_NO_FSTRING8) #if !defined(DQN_NO_FARRAY) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays +// ================================================================================================= DQN_FARRAY_API DQN_FARRAY Dqn_FArray_Init(T const *item, Dqn_isize count) { @@ -4364,9 +4412,9 @@ Dqn_BinarySearch(T const *array, #if defined(DQN_IMPLEMENTATION) #if defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset +// ================================================================================================= #pragma comment(lib, "bcrypt") #pragma comment(lib, "wininet") @@ -4706,12 +4754,13 @@ Dqn_BinarySearch(T const *array, #include // O_RDONLY ... etc #include // FICLONE #include // ioctl + #include // pid_t #include // getrandom #include // stat #include // sendfile #include // mmap #include // clock_gettime, nanosleep - #include // access + #include // access, gettid #endif Dqn_Library dqn_library; @@ -4799,9 +4848,9 @@ DQN_API void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator alloca } } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-LLOG] Dqn_Log | | Library logging -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$LLOG] Dqn_Log | | Library logging +// ================================================================================================= DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args) { (void)log_type; @@ -5869,9 +5918,9 @@ bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs) } #endif -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-PRIN] Dqn_Print | | Printing -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$PRIN] Dqn_Print | | Printing +// ================================================================================================= DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string) { DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err); @@ -5962,9 +6011,9 @@ DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...) } #if !defined(DQN_NO_DSMAP) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair +// ================================================================================================= DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64) { Dqn_DSMapKey result = {}; @@ -5997,9 +6046,21 @@ DQN_API bool operator==(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs) #endif // !defined(DQN_NO_DSMAP) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-DLIB] Dqn_Library | | Library run-time behaviour configuration -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$DLIB] Dqn_Library | | Library run-time behaviour configuration +// ================================================================================================= +DQN_API Dqn_Library *Dqn_Library_Init(Dqn_Arena *arena) +{ + Dqn_Library *result = &dqn_library; + Dqn_TicketMutex_Begin(&result->lib_mutex); + if (!result->lib_init) { + Dqn_ArenaCatalog_Init(&result->arena_catalog, arena ? arena : &result->arena_catalog_backup_arena); + result->lib_init = true; + } + Dqn_TicketMutex_End(&result->lib_mutex); + return result; +} + DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data) { dqn_library.log_callback = proc; @@ -6142,9 +6203,9 @@ DQN_API void Dqn_Library_LeakTraceMarkFree(Dqn_CallSite call_site, void *ptr) #endif /// defined(DQN_LEAK_TRACING) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-STRB] Dqn_String8Builder | | -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$STRB] Dqn_String8Builder | | +// ================================================================================================= bool Dqn_String8Builder_AppendRef(Dqn_String8Builder *builder, Dqn_String8 string) { if (!builder || !string.data || string.size <= 0) @@ -6217,7 +6278,7 @@ Dqn_String8 Dqn_String8Builder_Build(Dqn_String8Builder const *builder, Dqn_Allo } // ================================================================================================= -// [SECT-AREN] Dqn_Arena | | Growing bump allocator +// [$AREN] Dqn_Arena | | Growing bump allocator // ================================================================================================= DQN_API void *Dqn_Arena_AllocateFromBlock(Dqn_ArenaBlock *block, Dqn_isize size, uint8_t align, Dqn_ZeroMem zero_mem) { @@ -6543,10 +6604,72 @@ DQN_API void Dqn_Arena_Free_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroM arena->stats.blocks = 0; } +// ================================================================================================= +// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog +// ================================================================================================= +DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena) +{ + catalog->arena = arena; + catalog->sentinel.next = &catalog->sentinel; + catalog->sentinel.prev = &catalog->sentinel; +} + +DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, char const *fmt, va_list args) +{ + // NOTE: We could use an atomic for appending to the sentinel but it is such + // a rare operation to append to the catalog that we don't bother. + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); + + // NOTE: Create item in the catalog + Dqn_ArenaCatalogItem *result = Dqn_Arena_New(catalog->arena, Dqn_ArenaCatalogItem, Dqn_ZeroMem_Yes); + result->arena = arena; + + // NOTE: Create arena label + result->label = Dqn_String8_InitFV(Dqn_Arena_Allocator(catalog->arena), fmt, args); + + // NOTE: Add to the catalog (linked list) + Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel; + result->next = sentinel; + result->prev = sentinel->prev; + result->next->prev = result; + result->prev->next = result; + Dqn_TicketMutex_End(&catalog->ticket_mutex); + + Dqn_Atomic_AddU32(&catalog->arena_count, 1); +} + +DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + Dqn_ArenaCatalog_AddFV(catalog, arena, fmt, args); + va_end(args); +} + +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args) +{ + Dqn_TicketMutex_Begin(&catalog->ticket_mutex); + Dqn_Arena *result = Dqn_Arena_New(catalog->arena, Dqn_Arena, Dqn_ZeroMem_Yes); + Dqn_TicketMutex_End(&catalog->ticket_mutex); + + Dqn_Arena_Grow(result, byte_size, commit, 0 /*flags*/); + Dqn_ArenaCatalog_AddFV(catalog, result, fmt, args); + return result; +} + +DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + Dqn_Arena *result = Dqn_ArenaCatalog_AllocFV(catalog, byte_size, commit, fmt, args); + va_end(args); + return result; +} + #if !defined(DQN_NO_MATH) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp +// ================================================================================================= DQN_API Dqn_V2I Dqn_V2ToV2I(Dqn_V2 a) { Dqn_V2I result = Dqn_V2I(DQN_CAST(int32_t)a.x, DQN_CAST(int32_t)a.y); @@ -6995,9 +7118,9 @@ DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b) } #endif // !defined(DQN_NO_MATH) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-BITS] Dqn_Bit | | Bitset manipulation -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$BITS] Dqn_Bit | | Bitset manipulation +// ================================================================================================= DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield) { *flags = (*flags & ~bitfield); @@ -7020,9 +7143,9 @@ DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts +// ================================================================================================= DQN_API bool Dqn_Safe_AssertF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...) { bool result = assertion_expr; @@ -7357,9 +7480,9 @@ DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-CHAR] Dqn_Char | | Character ascii/digit.. helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers +// ================================================================================================= DQN_API bool Dqn_Char_IsAlpha(char ch) { bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); @@ -7426,9 +7549,9 @@ DQN_API char Dqn_Char_ToLower(char ch) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-UTFX] Dqn_UTF | | Unicode helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$UTFX] Dqn_UTF | | Unicode helpers +// ================================================================================================= DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint) { // NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/ @@ -7502,9 +7625,9 @@ DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint) } #if !defined(DQN_NO_HEX) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$BHEX] Dqn_Hex | DQN_NO_HEX | Binary <-> hex helpers +// ================================================================================================= DQN_API char const *Dqn_Hex_TrimSpaceAnd0xPrefixCString8(char const *hex, Dqn_isize size, Dqn_isize *real_size) { Dqn_isize trim_size = 0; @@ -7744,9 +7867,9 @@ DQN_API Dqn_String8 Dqn_Hex_String8ToBytesArena(Dqn_Arena *arena, Dqn_String8 he } #endif // !defined(DQN_NO_HEX) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-DATE] Dqn_Date | | Date-time helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$DATE] Dqn_Date | | Date-time helpers +// ================================================================================================= DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow() { Dqn_DateHMSTime result = {}; @@ -7791,16 +7914,16 @@ DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString(Dqn_DateHMSTime time, { Dqn_DateHMSTimeString result = {}; result.hms_size = DQN_CAST(uint8_t) STB_SPRINTF_DECORATE(snprintf)(result.hms, - DQN_CAST(int) Dqn_CArray_CountI(result.hms), - "%02d%c%02d%c%02d", - time.hour, - hms_separator, - time.minutes, - hms_separator, - time.seconds); + DQN_ARRAY_ICOUNT(result.hms), + "%02d%c%02d%c%02d", + time.hour, + hms_separator, + time.minutes, + hms_separator, + time.seconds); result.date_size = DQN_CAST(uint8_t) STB_SPRINTF_DECORATE(snprintf)(result.date, - DQN_CAST(int) Dqn_CArray_CountI(result.date), + DQN_ARRAY_ICOUNT(result.date), "%d%c%02d%c%02d", time.year, date_separator, @@ -7850,9 +7973,9 @@ DQN_API uint64_t Dqn_Date_EpochTime() #endif #if defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-WIND] Dqn_Win | | Windows OS helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$WIND] Dqn_Win | | Windows OS helpers +// ================================================================================================= DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg) { msg->code = GetLastError(); @@ -8111,7 +8234,7 @@ DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW * continue; it->file_name.size = Dqn_CString16_Size(find_data.cFileName); - DQN_ASSERT(it->file_name.size < (Dqn_CArray_CountI(it->file_name_buf) - 1)); + DQN_ASSERT(it->file_name.size < (DQN_ARRAY_UCOUNT(it->file_name_buf) - 1)); DQN_MEMCPY(it->file_name.data, find_data.cFileName, it->file_name.size * sizeof(wchar_t)); it->file_name_buf[it->file_name.size] = 0; break; @@ -8173,9 +8296,9 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it) } #if !defined(DQN_NO_WIN_NET) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers +// ================================================================================================= DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_size) { URL_COMPONENTSA components = {}; @@ -8568,9 +8691,9 @@ DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString(Dqn_WinNetHandle *handle) #endif // !defined(DQN_NO_WINNET) #endif // defined(DQN_OS_WIN32) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs +// ================================================================================================= DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size) { if (!buffer || size < 0) @@ -8833,9 +8956,9 @@ DQN_API Dqn_f64 Dqn_OS_TimerNs(Dqn_OSTimer timer) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FSYS] Dqn_Fs | | Filesystem helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FSYS] Dqn_Fs | | Filesystem helpers +// ================================================================================================= #if defined(DQN_OS_WIN32) DQN_API uint64_t Dqn__WinFileTimeToSeconds(FILETIME const *time) { @@ -9512,9 +9635,9 @@ DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file) *file = {}; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MISC] Miscellaneous | | General purpose helpers -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MISC] Miscellaneous | | General purpose helpers +// ================================================================================================= DQN_API int Dqn_SNPrintF2DotsOnOverflow(char *buffer, int size, char const *fmt, ...) { va_list args; @@ -9559,14 +9682,14 @@ DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas +// ================================================================================================= Dqn_ThreadScratch::Dqn_ThreadScratch(DQN_LEAK_TRACE_FUNCTION Dqn_ThreadContext *context, uint8_t context_index) { index = context_index; allocator = context->temp_allocators[index]; - arena = &context->temp_arenas[index]; + arena = context->temp_arenas[index]; temp_memory = Dqn_Arena_BeginTempMemory(arena); #if defined(DQN_LEAK_TRACING) leak_site__ = DQN_LEAK_TRACE_ARG_NO_COMMA; @@ -9587,17 +9710,41 @@ Dqn_ThreadScratch::~Dqn_ThreadScratch() destructed = true; } +DQN_API uint32_t Dqn_Thread_GetID() +{ + #if defined(DQN_OS_WIN32) + unsigned long result = GetCurrentThreadId(); + #else + pid_t result = gettid(); + assert(gettid() >= 0); + #endif + return (uint32_t)result; +} + DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA) { thread_local Dqn_ThreadContext result = {}; if (!result.init) { result.init = true; - Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG &result.arena, DQN_MEGABYTES(4), DQN_MEGABYTES(4), 0 /*flags*/); - result.allocator = Dqn_Arena_Allocator(&result.arena); + + // NOTE: Setup permanent arena + Dqn_ArenaCatalog *catalog = &dqn_library.arena_catalog; + result.allocator = Dqn_Arena_Allocator(result.arena); + result.arena = Dqn_ArenaCatalog_AllocF(catalog, + DQN_GIGABYTES(1) /*size*/, + DQN_KILOBYTES(64) /*commit*/, + "Thread %u Arena", + Dqn_Thread_GetID()); + + // NOTE: Setup temporary arenas for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) { - Dqn_Arena *arena = result.temp_arenas + index; - result.temp_allocators[index] = Dqn_Arena_Allocator(arena); - Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG arena, DQN_MEGABYTES(4), DQN_MEGABYTES(4), 0 /*flags*/); + result.temp_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog, + DQN_GIGABYTES(1) /*size*/, + DQN_KILOBYTES(64) /*commit*/, + "Thread %u Temp Arena %u", + Dqn_Thread_GetID(), + index); + result.temp_allocators[index] = Dqn_Arena_Allocator(result.temp_arenas[index]); } } return &result; @@ -9611,7 +9758,7 @@ DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void co Dqn_ThreadContext *context = Dqn_Thread_GetContext_(DQN_LEAK_TRACE_ARG_NO_COMMA); uint8_t context_index = (uint8_t)-1; for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) { - Dqn_Arena *arena = context->temp_arenas + index; + Dqn_Arena *arena = context->temp_arenas[index]; if (!conflict_arena || arena != conflict_arena) { context_index = index; break; @@ -9622,10 +9769,10 @@ DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void co return Dqn_ThreadScratch(DQN_LEAK_TRACE_ARG context, context_index); } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output -// ---------------------------------+-----------------------------+--------------------------------- #if !defined(DQN_NO_JSON_BUILDER) +// ================================================================================================= +// [$JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output +// ================================================================================================= Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces_per_indent) { Dqn_JSONBuilder result = {}; @@ -9784,9 +9931,9 @@ void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, bool v #endif // !defined(DQN_NO_JSON_BUILDER) -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a +// ================================================================================================= // Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/ DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_isize size, uint32_t hash) { @@ -9816,9 +9963,9 @@ DQN_API uint64_t Dqn_FNV1A64_Hash(void const *bytes, Dqn_isize size) return result; } -// ---------------------------------+-----------------------------+--------------------------------- -// [SECT-MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 -// ---------------------------------+-----------------------------+--------------------------------- +// ================================================================================================= +// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 +// ================================================================================================= #if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #define DQN_MMH3_ROTL32(x, y) _rotl(x, y) #define DQN_MMH3_ROTL64(x, y) _rotl64(x, y) diff --git a/dqn_cpp_file.h b/dqn_cpp_file.h index 252127a..bfd0a88 100644 --- a/dqn_cpp_file.h +++ b/dqn_cpp_file.h @@ -7,63 +7,149 @@ #include /// va_list... #include /// assert -struct Dqn_CppFile /// Maintains state for printing C++ style formatted files -{ - FILE *file; ///< File to print to - int indent; ///< Current indent level of the printer - int space_per_indent; ///< Number of spaces per indent - bool k_and_r_indent; ///< K&R style indenting when opening a block scope, e.g. "{\n" vs "\n{" -}; +typedef struct Dqn_CppFile { ///< Maintains state for printing C++ style formatted files + FILE *file; ///< (Write) File to print to + int indent; ///< Current indent level of the printer + int space_per_indent; ///< (Write) Number of spaces per indent + unsigned char if_chain[256]; /// + unsigned char if_chain_size; /// +} Dqn_CppFile; /// Print the format string indented and terminate the string with a new-line. -void Dqn_CppFLineV(Dqn_CppFile *cpp, char const *fmt, va_list args); -void Dqn_CppFLine(Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppLineV(Dqn_CppFile *cpp, char const *fmt, va_list args); +void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...); /// Print the format string indented -void Dqn_CppFPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args); -void Dqn_CppFPrint(Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args); +void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...); /// Print the format string -#define Dqn_CppFAppend(cpp, fmt, ...) vfprintf(cpp->file, fmt, ##__VAR_ARGS__) +#define Dqn_CppAppend(cpp, fmt, ...) fprintf((cpp)->file, fmt, ##__VA_ARGS__) +#define Dqn_CppAppendV(cpp, fmt, args) vfprintf((cpp)->file, fmt, args) -/// End the current line, useful after CppFPrint and CppFAppend -#define Dqn_CppFEndLine(cpp) fputc('\n', (cpp)->file) +/// End the current line, useful after CppPrint and CppAppend +#define Dqn_CppNewLine(cpp) fputc('\n', (cpp)->file) /// Manually modify the indent level -#define Dqn_CppFIndent(cpp) (cpp)->indent++; -#define Dqn_CppFUnindent(cpp) (cpp)->indent--; assert(cpp->indent >= 0) +#define Dqn_CppIndent(cpp) (cpp)->indent++ +#define Dqn_CppUnindent(cpp) (cpp)->indent--; assert((cpp)->indent >= 0) + +/// Block scope functions that execute a function on entry and exit of the +/// scope by exploiting the comma operator and a for loop. +/// +/// @code +/// Dqn_CppEnumBlock(cpp, "abc") { +/// printf("Hello world!"); +/// } +/// +/// // Is equivalent to +/// +/// Dqn_CppBeginBlock(cpp, "abc"); +/// printf("Hello world!"); +/// Dqn_CppEndEnumBlock(cpp); +/// @endcode + +#define Dqn_CppEnumBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndEnumBlock(cpp), false)) + +#define Dqn_CppForBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false)) + +#define Dqn_CppWhileBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false)) + +#define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginIfOrElseIfBlock(cpp, fmt, ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndIfOrElseIfBlock(cpp), false)) + +#define Dqn_CppElseBlock(cpp) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginElseBlock(cpp), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndElseBlock(cpp), false)) + +#define Dqn_CppFuncBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndFuncBlock(cpp), false)) + +#define Dqn_CppStructBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndStructBlock(cpp), false)) + +#define Dqn_CppSwitchBlock(cpp, fmt, ...) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ + (Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndSwitchBlock(cpp), false)) + +#define Dqn_CppIfChain(cpp) \ + for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ + DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndIfChain(cpp), false)) /// Print the format string followed by a "{" and enter a new line whilst /// increasing the indent level after the brace. -void Dqn_CppFBeginBlock(Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFEndBlock (Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block); +void Dqn_CppBeginBlock (Dqn_CppFile *cpp, bool append, char const *fmt, ...); +void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list args); +void Dqn_CppEndBlock (Dqn_CppFile *cpp); -/// End a block, specifically for the following language constructs. -#define Dqn_CppFEndEnumBlock(cpp) Dqn_CppFEndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) -#define Dqn_CppFEndForBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) -#define Dqn_CppFEndIfBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) -#define Dqn_CppFEndFuncBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, true /*new_line_on_next_block*/) -#define Dqn_CppFEndStructBlock(cpp) Dqn_CppFEndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) +/// Begin/End a block, specifically for the following language constructs. +#define Dqn_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) +#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n") + +#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) +#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n") + +#define Dqn_CppBeginFuncBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) +#define Dqn_CppEndFuncBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n") + +#define Dqn_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) +#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n") + +#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) +#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n") + +void Dqn_CppBeginIfOrElseIfBlock (Dqn_CppFile *cpp, char const *fmt, ...); +#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp) + +void Dqn_CppBeginElseBlock (Dqn_CppFile *cpp); +#define Dqn_CppEndElseBlock(cpp) Dqn_CppEndBlock(cpp) + +#define DQN_CPP_TOKEN_PASTE2_(x, y) x ## y +#define DQN_CPP_TOKEN_PASTE_(x, y) DQN_CPP_TOKEN_PASTE2_(x, y) #endif // DQN_CPP_FILE_H #if defined(DQN_CPP_FILE_IMPLEMENTATION) -// NOTE: Dqn_CppFile Implementation -// ============================================================================= -void Dqn_CppFLineV(Dqn_CppFile *cpp, char const *fmt, va_list args) +void Dqn_CppLineV(Dqn_CppFile *cpp, char const *fmt, va_list args) { - Dqn_CppFPrintV(cpp, fmt, args); - Dqn_CppFLineEnd(cpp, nullptr); + Dqn_CppPrintV(cpp, fmt, args); + Dqn_CppNewLine(cpp); } -void Dqn_CppFLine(Dqn_CppFile *cpp, char const *fmt, ...) +void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...) { va_list args; va_start(args, fmt); - Dqn_CppFLineV(cpp, fmt, args); + Dqn_CppLineV(cpp, fmt, args); va_end(args); } -void Dqn_CppFPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args) +void Dqn_CppPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args) { int space_per_indent = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent; int spaces = fmt ? (cpp->indent * space_per_indent) : 0; @@ -71,29 +157,71 @@ void Dqn_CppFPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args) vfprintf(cpp->file, fmt, args); } -void Dqn_CppFPrint(Dqn_CppFile *cpp, char const *fmt, ...) +void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...) { va_list args; va_start(args, fmt); - Dqn_CppFPrintV(cpp, fmt, args); + Dqn_CppPrintV(cpp, fmt, args); va_end(args); } -void Dqn_CppFBeginBlock(Dqn_CppFile *cpp, char const *fmt, ...) +void Dqn_CppBeginBlock(Dqn_CppFile *cpp, bool append, char const *fmt, ...) { va_list args; va_start(args, fmt); - Dqn_CppFLineV(cpp, fmt, args); + Dqn_CppBeginBlockV(cpp, append, fmt, args); va_end(args); - Dqn_CppFPrint(cpp, "%s", cpp->k_and_r_indent ? "{\n" : "\n{"); - Dqn_CppFIndent(cpp); } -void Dqn_CppFEndBlock(Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block) +void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list args) { - Dqn_CppFUnindent(cpp); - Dqn_CppFLine(cpp, trailing_semicolon ? "};" : "}"); - if (new_line_on_next_block) - fputc('\n', cpp->file); + if (append) + Dqn_CppAppendV(cpp, fmt, args); + else + Dqn_CppPrintV(cpp, fmt, args); + Dqn_CppAppend(cpp, " {\n"); + Dqn_CppIndent(cpp); } + +void Dqn_CppEndBlock(Dqn_CppFile *cpp) +{ + Dqn_CppUnindent(cpp); + Dqn_CppPrint(cpp, "}"); +} + +void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (cpp->if_chain[cpp->if_chain_size - 1] == 0) + Dqn_CppPrint(cpp, "if"); + else + Dqn_CppAppend(cpp, " else if"); + + Dqn_CppAppend(cpp, " ("); + Dqn_CppAppendV(cpp, fmt, args); + Dqn_CppAppend(cpp, ") {\n"); + Dqn_CppIndent(cpp); + va_end(args); + cpp->if_chain[cpp->if_chain_size - 1]++; +} + +void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp) +{ + if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) + Dqn_CppBeginBlock(cpp, true /*append*/, " else"); +} + +void Dqn_CppBeginIfChain(Dqn_CppFile *cpp) +{ + assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0])); + cpp->if_chain_size++; +} + +void Dqn_CppEndIfChain(Dqn_CppFile *cpp) +{ + if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) + Dqn_CppNewLine(cpp); +} + #endif // DQN_CPP_FILE_IMPLEMENTATION diff --git a/dqn_json.h b/dqn_json.h index 1055dce..2f7603f 100644 --- a/dqn_json.h +++ b/dqn_json.h @@ -11,83 +11,6 @@ void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count); char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size); bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 rhs); -#define Dqn_JSON_RecordIgnoredObjElement(element, flags) Dqn_JSON_RecordIgnoredObjElement_(element, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -void Dqn_JSON_RecordIgnoredObjElement_(json_object_element_s const *element, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_RecordIgnoredArrayElement(element, flags) Dqn_JSON_RecordIgnoredArrayElement_(element, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -void Dqn_JSON_RecordIgnoredArrayElement_(json_array_element_s const *element, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_RecordIgnoredValue(value, flags) Dqn_JSON_RecordIgnoredValue_(value, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -void Dqn_JSON_RecordIgnoredValue_(json_value_s const *value, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -// NOTE: Dqn_JSON_ValueCheck -// ----------------------------------------------------------------------------- -#define Dqn_JSON_ValueCheck(val, type, flags, require) Dqn_JSON_ValueCheck(val, type, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_ValueCheck_(json_value_s *val, json_type_e type, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ValueCheckObj(val, flags, require) Dqn_JSON_ValueCheckObj_(val, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_object_s *Dqn_JSON_ValueCheckObj_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ValueCheckArray(val, flags, require) Dqn_JSON_ValueCheckArray_(val, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_array_s *Dqn_JSON_ValueCheckArray_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ValueCheckString(val, flags, require) Dqn_JSON_ValueCheckString_(val, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_string_s *Dqn_JSON_ValueCheckString_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ValueCheckNumber(val, flags, require) Dqn_JSON_ValueCheckNumber_(val, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_number_s *Dqn_JSON_ValueCheckNumber_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ValueCheckBool(val, flags, require) Dqn_JSON_ValueCheckBool_(val, flags, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_ValueCheckBool_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - - -// NOTE: Dqn_JSON_ValueRequire -// ----------------------------------------------------------------------------- -#define Dqn_JSON_ValueRequire(val, type, flags) Dqn_JSON_ValueCheck(val, type, flags, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ValueRequireObj(val, flags) Dqn_JSON_ValueCheckObj_(val, flags, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ValueRequireArray(val, flags) Dqn_JSON_ValueCheckArray_(val, flags, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ValueRequireString(val, flags) Dqn_JSON_ValueCheckString_(val, flags, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ValueRequireNumber(val, flags) Dqn_JSON_ValueCheckNumber_(val, flags, true /*require*/ DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ValueRequireBool(val, flags) Dqn_JSON_ValueCheckBool_(val, flags, true /*require*/ DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) - -// NOTE: Dqn_JSON_ObjElementCheck -// ----------------------------------------------------------------------------- -#define Dqn_JSON_ObjElementCheckNamed(element, type, key, flags) Dqn_JSON_ObjElementCheckNamed_(element, type, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_ObjElementCheckNamed_(json_object_element_s *element, json_type_e type, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ObjElementCheckNamedObj(element, key, flags) Dqn_JSON_ObjElementCheckNamedObj_(element, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_object_s *Dqn_JSON_ObjElementCheckNamedObj_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ObjElementCheckNamedArray(element, key, flags) Dqn_JSON_ObjElementCheckNamedArray_(element, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_array_s *Dqn_JSON_ObjElementCheckNamedArray_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ObjElementCheckNamedString(element, key, flags) Dqn_JSON_ObjElementCheckNamedString_(element, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_string_s *Dqn_JSON_ObjElementCheckNamedString_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ObjElementCheckNamedNumber(element, key, flags) Dqn_JSON_ObjElementCheckNamedNumber_(element, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_number_s *Dqn_JSON_ObjElementCheckNamedNumber_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_ObjElementCheckNamedBool(element, key, flags) Dqn_JSON_ObjElementCheckNamedBool_(element, key, flags, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_ObjElementCheckNamedBool_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -// NOTE: Dqn_JSON_ObjElementIs -// ----------------------------------------------------------------------------- -#define Dqn_JSON_ObjElementIsNamed(element, type, key) Dqn_JSON_ObjElementCheckNamed_(element, type, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementIsNamedObj(element, key) Dqn_JSON_ObjElementCheckNamedObj_(element, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementIsNamedArray(element, key) Dqn_JSON_ObjElementCheckNamedArray_(element, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementIsNamedString(element, key) Dqn_JSON_ObjElementCheckNamedString_(element, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementIsNamedNumber(element, key) Dqn_JSON_ObjElementCheckNamedNumber_(element, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementIsNamedBool(element, key) Dqn_JSON_ObjElementCheckNamedBool_(element, key, 0 /*flags*/, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) - -// NOTE: Dqn_JSON_ObjElementRequire -// ----------------------------------------------------------------------------- -#define Dqn_JSON_ObjElementRequireNamed(element, type, key) Dqn_JSON_ObjElementCheckNamed_(element, type, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementRequireNamedObj(element, key) Dqn_JSON_ObjElementCheckNamedObj_(element, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementRequireNamedArray(element, key) Dqn_JSON_ObjElementCheckNamedArray_(element, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementRequireNamedString(element, key) Dqn_JSON_ObjElementCheckNamedString_(element, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementRequireNamedNumber(element, key) Dqn_JSON_ObjElementCheckNamedNumber_(element, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_ObjElementRequireNamedBool(element, key) Dqn_JSON_ObjElementCheckNamedBool_(element, key, 0 /*flags*/, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) - // NOTE: Dqn_JSON_Iterator // ----------------------------------------------------------------------------- enum Dqn_JSONIteratorEntryType @@ -115,91 +38,63 @@ struct Dqn_JSONIterator // NOTE: Dqn_JSON_IteratorPush/Pop // ----------------------------------------------------------------------------- -bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *state, json_object_element_s *element); -bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *state, json_object_s *obj); -bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *state, json_array_element_s *element); -bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *state, json_array_s *array); -void Dqn_JSON_IteratorPop(Dqn_JSONIterator *state); +bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *it, json_object_element_s *element); +bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *it, json_object_s *obj); +bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *it, json_array_element_s *element); +bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *it, json_array_s *array); +bool Dqn_JSON_IteratorPushValue(Dqn_JSONIterator *it, json_value_s *value); +void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it); // NOTE: Dqn_JSON_Iterator tree navigation // ----------------------------------------------------------------------------- -json_value_s *Dqn_JSON_IteratorPushKey(Dqn_JSONIterator *state); -bool Dqn_JSON_IteratorNextField(Dqn_JSONIterator *state); +json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it); +bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it); -#define Dqn_JSON_IteratorErrorUnrecognisedKey(state) Dqn_JSON_IteratorErrorUnrecognisedKey_(state, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -void Dqn_JSON_IteratorErrorUnrecognisedKey_(Dqn_JSONIterator *state, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); +#define Dqn_JSON_IteratorErrorUnrecognisedKey(it) Dqn_JSON_IteratorErrorUnrecognisedKey_(it, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) +void Dqn_JSON_IteratorErrorUnrecognisedKey_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); -#define Dqn_JSON_IteratorPushKeyIterateThenPop(state) \ - for(void *DQN_UNIQUE_NAME(ptr) = Dqn_JSON_IteratorPushKey(state); DQN_UNIQUE_NAME(ptr); Dqn_JSON_IteratorPop(state), DQN_UNIQUE_NAME(ptr) = nullptr) \ - while (Dqn_JSON_IteratorNextField(state)) +#define Dqn_JSON_IteratorPushCurrValueIterateThenPop(it) \ + for(void *DQN_UNIQUE_NAME(ptr) = Dqn_JSON_IteratorPushCurrValue(it); DQN_UNIQUE_NAME(ptr); Dqn_JSON_IteratorPop(it), DQN_UNIQUE_NAME(ptr) = nullptr) \ + while (Dqn_JSON_IteratorNext(it)) // NOTE: Dqn_JSON_IteratorCurr // ----------------------------------------------------------------------------- -Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *state); -json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *state); -json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *state); +Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it); +json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it); +json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it); -// NOTE: Dqn_JSON_IteratorKeyCheck +// NOTE: Dqn_JSON_IteratorValueIs // ----------------------------------------------------------------------------- +json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *it, json_type_e type); +json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it); +json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it); +json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it); +json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it); +json_value_s *Dqn_JSON_IteratorValueIsBool(Dqn_JSONIterator *it); -#define Dqn_JSON_IteratorKeyCheck(state, type, require) Dqn_JSON_IteratorKeyCheck(state, type, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_IteratorKeyCheck_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); +size_t Dqn_JSON_IteratorValueArraySize(Dqn_JSONIterator *it); -#define Dqn_JSON_IteratorKeyCheckObj(state, require) Dqn_JSON_IteratorKeyCheckObj_(state, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_object_s *Dqn_JSON_IteratorKeyCheckObj_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyCheckArray(state, require) Dqn_JSON_IteratorKeyCheckArray_(state, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_array_s *Dqn_JSON_IteratorKeyCheckArray_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyCheckString(state, require) Dqn_JSON_IteratorKeyCheckString_(state, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_string_s *Dqn_JSON_IteratorKeyCheckString_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyCheckNumber(state, require) Dqn_JSON_IteratorKeyCheckNumber_(state, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_number_s *Dqn_JSON_IteratorKeyCheckNumber_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyCheckBool(state, require) Dqn_JSON_IteratorKeyCheckBool_(state, require, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_IteratorKeyCheckBool_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -// NOTE: Dqn_JSON_Iterator_KeyIs +// NOTE: Dqn_JSON_IteratorKeyValueIs // ----------------------------------------------------------------------------- -#define Dqn_JSON_IteratorKeyIs(state, type) Dqn_JSON_IteratorKeyCheck(state, type, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyIsObj(state) Dqn_JSON_IteratorKeyCheckObj_(state, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyIsArray(state) Dqn_JSON_IteratorKeyCheckArray_(state, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyIsString(state) Dqn_JSON_IteratorKeyCheckString_(state, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyIsNumber(state) Dqn_JSON_IteratorKeyCheckNumber_(state, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyIsBool(state) Dqn_JSON_IteratorKeyCheckBool_(state, false /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) +Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it); +bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key); +json_object_s *Dqn_JSON_IteratorKeyValueIsObj(Dqn_JSONIterator *it, Dqn_String8 key); +json_array_s *Dqn_JSON_IteratorKeyValueIsArray(Dqn_JSONIterator *it, Dqn_String8 key); +json_string_s *Dqn_JSON_IteratorKeyValueIsString(Dqn_JSONIterator *it, Dqn_String8 key); +json_number_s *Dqn_JSON_IteratorKeyValueIsNumber(Dqn_JSONIterator *it, Dqn_String8 key); +json_value_s *Dqn_JSON_IteratorKeyValueIsBool(Dqn_JSONIterator *it, Dqn_String8 key); -// NOTE: Dqn_JSON_Iterator_KeyRequire +// NOTE: Dqn_JSON_IteratorValueTo // ----------------------------------------------------------------------------- -#define Dqn_JSON_IteratorKeyRequire(state, type, require) Dqn_JSON_IteratorKeyCheck(state, type, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyRequireObj(state, require) Dqn_JSON_IteratorKeyCheckObj_(state, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyRequireArray(state, require) Dqn_JSON_IteratorKeyCheckArray_(state, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyRequireString(state, require) Dqn_JSON_IteratorKeyCheckString_(state, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyRequireNumber(state, require) Dqn_JSON_IteratorKeyCheckNumber_(state, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -#define Dqn_JSON_IteratorKeyRequireBool(state, require) Dqn_JSON_IteratorKeyCheckBool_(state, true /*require*/, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) +Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it); +int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *it); +uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it); +bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it); -// NOTE: Dqn_JSON_IteratorKeyIsNamed -// ----------------------------------------------------------------------------- -#define Dqn_JSON_IteratorKeyIsNamedObj(state, key) Dqn_JSON_IteratorKeyIsNamedObj_(state, key, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_object_s *Dqn_JSON_IteratorKeyIsNamedObj_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); +#define Dqn_JSON_IteratorErrorUnknownKeyValue(it) \ + Dqn_JSON_IteratorErrorUnknownKeyValue_(it, DQN_CALL_SITE) -#define Dqn_JSON_IteratorKeyIsNamedArray(state, key) Dqn_JSON_IteratorKeyIsNamedArray_(state, key, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_array_s *Dqn_JSON_IteratorKeyIsNamedArray_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyIsNamedString(state, key) Dqn_JSON_IteratorKeyIsNamedString_(state, key, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_string_s *Dqn_JSON_IteratorKeyIsNamedString_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyIsNamedNumber(state, key) Dqn_JSON_IteratorKeyIsNamedNumber_(state, key, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_number_s *Dqn_JSON_IteratorKeyIsNamedNumber_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -#define Dqn_JSON_IteratorKeyIsNamedBool(state, key) Dqn_JSON_IteratorKeyIsNamedBool_(state, key, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__) -json_value_s *Dqn_JSON_IteratorKeyIsNamedBool_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); - -// NOTE: Dqn_JSON_IteratorKeyTo -// ----------------------------------------------------------------------------- -Dqn_String8 Dqn_JSON_IteratorKeyToString(Dqn_JSONIterator *state); -int64_t Dqn_JSON_IteratorKeyToI64(Dqn_JSONIterator *state); -bool Dqn_JSON_IteratorKeyToBool(Dqn_JSONIterator *state); +void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, int line); #endif // DQN_JSON_H @@ -242,323 +137,74 @@ bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 key) return result; } -void Dqn_JSON_RecordIgnoredObjElement_(json_object_element_s const *element, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_string_s const *key = element->name; - size_t value_type_size = 0; - char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)element->value->type, &value_type_size); - if (flags == json_parse_flags_allow_location_information) { - auto const *info = DQN_CAST(json_string_ex_s const *)key; - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored key-value pair in object [line=%zu, col=%zu, key=%.*s, type=%.*s]", - info->line_no, - info->row_no, - key->string_size, - key->string, - value_type_size, - value_type); - } else { - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored key-value pair in object [key=%.*s, type=%.*s]", - key->string_size, - key->string, - value_type_size, - value_type); - } -} - -void Dqn_JSON_RecordIgnoredArrayElement_(json_array_element_s const *element, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_value_s const *value = element->value; - size_t size = 0; - char const *type = Dqn_JSON_TypeEnumCString((json_type_e)value->type, &size); - if (flags == json_parse_flags_allow_location_information) { - size_t line_no = 0; - size_t row_no = 0; - if (value->type == json_type_string) { - auto *info = DQN_CAST(json_string_ex_s const *) value; - line_no = info->line_no; - row_no = info->row_no; - } else { - auto *info = DQN_CAST(json_value_ex_s const *) value; - line_no = info->line_no; - row_no = info->row_no; - } - - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored value in array [line=%zu, col=%zu, value_type=%.*s]", - line_no, - row_no, - size, - type); - } else { - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored value in array [value_type=%.*s]", - size, - type); - } -} - -void Dqn_JSON_RecordIgnoredValue_(json_value_s const *value, size_t flags, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - size_t size = 0; - char const *type = Dqn_JSON_TypeEnumCString((json_type_e)value->type, &size); - if (flags == json_parse_flags_allow_location_information) { - size_t line_no = 0; - size_t row_no = 0; - if (value->type == json_type_string) { - auto *info = DQN_CAST(json_string_ex_s const *) value; - line_no = info->line_no; - row_no = info->row_no; - } else { - auto *info = DQN_CAST(json_value_ex_s const *) value; - line_no = info->line_no; - row_no = info->row_no; - } - - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored value in json [line=%zu, col=%zu, value_type=%.*s]", - line_no, - row_no, - size, - type); - } else { - Dqn_Log(Dqn_LogType::Warning, - nullptr, - file, - func, - line, - "Ignored value in json [value_type=%.*s]", - size, - type); - } -} - -// NOTE: Dqn_JSON_ValueCheck -// ----------------------------------------------------------------------------- -json_value_s *Dqn_JSON_ValueCheck_(json_value_s *val, json_type_e type, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_value_s *result = (val && val->type == type) ? val : nullptr; - if (!result && require) { - size_t expect_size = 0; - char const *expect_type = Dqn_JSON_TypeEnumCString(type, &expect_size); - size_t val_size = 0; - char const *val_type = Dqn_JSON_TypeEnumCString((json_type_e)val->type, &val_size); - if (flags == json_parse_flags_allow_location_information) { - auto const *info = DQN_CAST(json_value_ex_s const *) val; - Dqn_Log(Dqn_LogType::Error, - nullptr /*user_data*/, - file, - func, - line, - "Expected json '%.*s' [line=%zu, col=%zu, value_type=%.*s]", - expect_size, - expect_type, - info->line_no, - info->row_no, - val_size, - val_type); - } else { - Dqn_Log(Dqn_LogType::Error, - nullptr /*userdata*/, - file, - func, - line, - "Expected json '%.*s' [value_type=%.*s]", - expect_size, - expect_type, - val_size, - val_type); - } - } - - return result; -} - -json_object_s *Dqn_JSON_ValueCheckObj_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_object_s *result = nullptr; - if (Dqn_JSON_ValueCheck_(val, json_type_object, flags, require, file, func, line)) - result = json_value_as_object(val); - return result; -} - -json_array_s *Dqn_JSON_ValueCheckArray_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_array_s *result = nullptr; - if (Dqn_JSON_ValueCheck_(val, json_type_array, flags, require, file, func, line)) - result = json_value_as_array(val); - return result; -} - -json_string_s *Dqn_JSON_ValueCheckString_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_string_s *result = nullptr; - if (Dqn_JSON_ValueCheck_(val, json_type_string, flags, require, file, func, line)) - result = json_value_as_string(val); - return result; -} - -json_number_s *Dqn_JSON_ValueCheckNumber_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_number_s *result = nullptr; - if (Dqn_JSON_ValueCheck_(val, json_type_number, flags, require, file, func, line)) - result = json_value_as_number(val); - return result; -} - -json_value_s *Dqn_JSON_ValueCheckBool_(json_value_s *val, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_value_s *result = nullptr; - if (val->type == json_type_true || val->type == json_type_false) { - result = val; - } else { - if (require) { - size_t val_size = 0; - char const *val_type = Dqn_JSON_TypeEnumCString((json_type_e)val->type, &val_size); - if (flags == json_parse_flags_allow_location_information) { - auto const *info = DQN_CAST(json_value_ex_s const *) val; - Dqn_Log(Dqn_LogType::Error, - nullptr /*userdata*/, - file, - func, - line, - "Expected json bool [line=%zu, col=%zu, value_type=%.*s]", - info->line_no, - info->row_no, - val_size, - val_type); - } else { - Dqn_Log(Dqn_LogType::Error, - nullptr /*userdata*/, - file, - func, - line, - "Expected json bool [value_type=%.*s]", - val_size, - val_type); - } - } - } - - return result; -} - -// NOTE: Dqn_JSON_ObjElementCheck -// ----------------------------------------------------------------------------- -json_value_s *Dqn_JSON_ObjElementCheckNamed_(json_object_element_s *element, json_type_e type, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_value_s *result = nullptr; - if (element && Dqn_JSON_String8Cmp(element->name, key)) - result = Dqn_JSON_ValueCheck_(element->value, type, flags, require, file, func, line); - return result; -} - -json_object_s *Dqn_JSON_ObjElementCheckNamedObj_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_object_s *result = DQN_CAST(json_object_s *)Dqn_JSON_ObjElementCheckNamed_(element, json_type_object, key, flags, require, file, func, line); - return result; -} - -json_array_s *Dqn_JSON_ObjElementCheckNamedArray_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_array_s *result = DQN_CAST(json_array_s *)Dqn_JSON_ObjElementCheckNamed_(element, json_type_array, key, flags, require, file, func, line); - return result; -} - -json_string_s *Dqn_JSON_ObjElementCheckNamedString_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_string_s *result = DQN_CAST(json_string_s *)Dqn_JSON_ObjElementCheckNamed_(element, json_type_string, key, flags, require, file, func, line); - return result; -} - -json_number_s *Dqn_JSON_ObjElementCheckNamedNumber_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_number_s *result = DQN_CAST(json_number_s *)Dqn_JSON_ObjElementCheckNamed_(element, json_type_number, key, flags, require, file, func, line); - return result; -} - -json_value_s *Dqn_JSON_ObjElementCheckNamedBool_(json_object_element_s *element, Dqn_String8 key, size_t flags, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - json_value_s *result = nullptr; - if (element && Dqn_JSON_String8Cmp(element->name, key)) - result = Dqn_JSON_ValueCheckBool_(element->value, flags, require, file, func, line); - return result; -} - // NOTE: Dqn_JSON_Iterator_push/pop // ----------------------------------------------------------------------------- -bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *state, json_object_element_s *element) +bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *it, json_object_element_s *element) { - if (!state || !element) + if (!it || !element) return false; - DQN_ASSERT(state->stack_count < DQN_ARRAY_ICOUNT(state->stack)); - state->stack[state->stack_count++] = {Dqn_JSON_IteratorEntryTypeObjElement, element}; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeObjElement, element}; return true; } -bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *state, json_object_s *obj) +bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *it, json_object_s *obj) { - if (!state || !obj) + if (!it || !obj) return false; - DQN_ASSERT(state->stack_count < DQN_ARRAY_ICOUNT(state->stack)); - state->stack[state->stack_count++] = {Dqn_JSON_IteratorEntryTypeObj, obj}; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeObj, obj}; return true; } -bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *state, json_array_element_s *element) +bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *it, json_array_element_s *element) { - if (!state || !element) + if (!it || !element) return false; - DQN_ASSERT(state->stack_count < DQN_ARRAY_ICOUNT(state->stack)); - state->stack[state->stack_count++] = {Dqn_JSON_IteratorEntryTypeArrayElement, element}; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeArrayElement, element}; return true; } -bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *state, json_array_s *array) +bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *it, json_array_s *array) { - if (!state || !array) + if (!it || !array) return false; - DQN_ASSERT(state->stack_count < DQN_ARRAY_ICOUNT(state->stack)); - state->stack[state->stack_count++] = {Dqn_JSON_IteratorEntryTypeArray, array}; + DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); + it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeArray, array}; return true; } -void Dqn_JSON_IteratorPop(Dqn_JSONIterator *state) +bool Dqn_JSON_IteratorPushValue(Dqn_JSONIterator *it, json_value_s *value) { - if (!state) + bool result = false; + if (!it || !value) + return result; + + if (value->type == json_type_object) { + result = Dqn_JSON_IteratorPushObj(it, json_value_as_object(value)); + } else if (value->type == json_type_array) { + result = Dqn_JSON_IteratorPushArray(it, json_value_as_array(value)); + } + + return result; +} + +void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it) +{ + if (!it) return; - DQN_ASSERT(state->stack_count > 0); - if (state->stack_count > 0) - state->stack_count--; + DQN_ASSERT(it->stack_count > 0); + if (it->stack_count > 0) + it->stack_count--; } // NOTE: Dqn_JSON_Iterator json tree navigation // ----------------------------------------------------------------------------- -json_value_s *Dqn_JSON_IteratorPushKey(Dqn_JSONIterator *state) +json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it) { json_value_s *result = nullptr; - Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(state); + Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); if (!curr) return result; @@ -575,19 +221,22 @@ json_value_s *Dqn_JSON_IteratorPushKey(Dqn_JSONIterator *state) if (result->type == json_type_array) { json_array_s *array = json_value_as_array(result); DQN_ASSERT(array); - Dqn_JSON_IteratorPushArray(state, array); + Dqn_JSON_IteratorPushArray(it, array); } else if (result->type == json_type_object) { json_object_s *obj = json_value_as_object(result); DQN_ASSERT(obj); - Dqn_JSON_IteratorPushObj(state, obj); + Dqn_JSON_IteratorPushObj(it, obj); } return result; } -bool Dqn_JSON_IteratorNextField(Dqn_JSONIterator *state) +bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it) { - Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(state); + Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); + if (!curr) + return false; + json_object_element_s *obj_element = nullptr; json_array_element_s *array_element = nullptr; if (curr->type == Dqn_JSON_IteratorEntryTypeObj) { @@ -596,61 +245,43 @@ bool Dqn_JSON_IteratorNextField(Dqn_JSONIterator *state) } else if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) { auto *element = DQN_CAST(json_object_element_s *) curr->value; obj_element = element->next; - Dqn_JSON_IteratorPop(state); + Dqn_JSON_IteratorPop(it); } else if (curr->type == Dqn_JSON_IteratorEntryTypeArray) { auto *array = DQN_CAST(json_array_s *) curr->value; array_element = array->start; } else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) { auto *element = DQN_CAST(json_array_element_s *) curr->value; array_element = element->next; - Dqn_JSON_IteratorPop(state); + Dqn_JSON_IteratorPop(it); } else { - Dqn_JSON_IteratorPop(state); + Dqn_JSON_IteratorPop(it); } if (obj_element) - Dqn_JSON_IteratorPushObjElement(state, obj_element); + Dqn_JSON_IteratorPushObjElement(it, obj_element); else if (array_element) - Dqn_JSON_IteratorPushArrayElement(state, array_element); + Dqn_JSON_IteratorPushArrayElement(it, array_element); bool result = obj_element || array_element; return result; } -void Dqn_JSON_IteratorErrorUnrecognisedKey_(Dqn_JSONIterator *state, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) -{ - Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(state); - if (!curr) - return; - - if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) { - json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value; - Dqn_JSON_RecordIgnoredObjElement_(element, state->flags, file, func, line); - } else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) { - json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value; - Dqn_JSON_RecordIgnoredArrayElement_(element, state->flags, file, func, line); - } else { - json_value_s *value = DQN_CAST(json_value_s *) curr->value; - Dqn_JSON_RecordIgnoredValue_(value, state->flags, file, func, line); - } -} - // NOTE: Dqn_JSON_IteratorCurr // ----------------------------------------------------------------------------- -Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *state) +Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it) { Dqn_JSONIteratorEntry *result = nullptr; - if (!state || state->stack_count <= 0) + if (!it || it->stack_count <= 0) return result; - result = &state->stack[state->stack_count - 1]; + result = &it->stack[it->stack_count - 1]; return result; } -json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *state) +json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it) { json_value_s *result = nullptr; - Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(state); + Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); if (!curr) return result; @@ -671,120 +302,200 @@ json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *state) return result; } -json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *state) +json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it) { - Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(state); - auto *result = (curr && curr->type == Dqn_JSON_IteratorEntryTypeObjElement) - ? DQN_CAST(json_object_element_s *) curr->value - : nullptr; + Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); + auto *result = (curr && curr->type == Dqn_JSON_IteratorEntryTypeObjElement) + ? DQN_CAST(json_object_element_s *) curr->value + : nullptr; return result; } -// NOTE: Dqn_JSON_IteratorKeyCheck +// NOTE: Dqn_JSON_IteratorValueIs // ----------------------------------------------------------------------------- -json_value_s *Dqn_JSON_IteratorKeyCheck_(Dqn_JSONIterator *state, json_type_e type, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *it, json_type_e type) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_value_s *result = Dqn_JSON_ValueCheck_(curr, type, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_value_s *result = (curr && type == curr->type) ? curr : nullptr; return result; } -json_object_s *Dqn_JSON_IteratorKeyCheckObj_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_object_s *result = Dqn_JSON_ValueCheckObj_(curr, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_object_s *result = curr ? json_value_as_object(curr) : nullptr; return result; } -json_array_s *Dqn_JSON_IteratorKeyCheckArray_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_array_s *result = Dqn_JSON_ValueCheckArray_(curr, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_array_s *result = curr ? json_value_as_array(curr) : nullptr; return result; } -json_string_s *Dqn_JSON_IteratorKeyCheckString_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_string_s *result = Dqn_JSON_ValueCheckString_(curr, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_string_s *result = curr ? json_value_as_string(curr) : nullptr; return result; } -json_number_s *Dqn_JSON_IteratorKeyCheckNumber_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_number_s *result = Dqn_JSON_ValueCheckNumber_(curr, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_number_s *result = curr ? json_value_as_number(curr) : nullptr; return result; } -json_value_s *Dqn_JSON_IteratorKeyCheckBool_(Dqn_JSONIterator *state, bool require, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_value_s *Dqn_JSON_IteratorValueIsBool(Dqn_JSONIterator *it) { - json_value_s *curr = Dqn_JSON_IteratorCurrValue(state); - json_value_s *result = Dqn_JSON_ValueCheckBool_(curr, state->flags, require, file, func, line); + json_value_s *curr = Dqn_JSON_IteratorCurrValue(it); + json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr; return result; } -// NOTE: Dqn_JSON_IteratorKeyIsNamed +size_t Dqn_JSON_IteratorValueArraySize(Dqn_JSONIterator *it) +{ + size_t result = 0; + if (json_array_s *curr = Dqn_JSON_IteratorValueIsArray(it)) + result = curr->length; + return result; +} + + +// NOTE: Dqn_JSON_IteratorKeyValueIs // ----------------------------------------------------------------------------- -json_object_s *Dqn_JSON_IteratorKeyIsNamedObj_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it) { - json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(state); - json_object_s *result = state ? Dqn_JSON_ObjElementCheckNamedObj_(curr, key, state->flags, false /*require*/, file, func, line) : nullptr; + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); + Dqn_String8 result = {}; + if (curr) { + result.data = DQN_CAST(char *)curr->name->string; + result.size = curr->name->string_size; + } return result; } -json_array_s *Dqn_JSON_IteratorKeyIsNamedArray_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key) { - json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(state); - json_array_s *result = state ? Dqn_JSON_ObjElementCheckNamedArray_(curr, key, state->flags, false /*require*/, file, func, line) : nullptr; + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); + bool result = Dqn_JSON_String8Cmp(curr->name, key); return result; } -json_string_s *Dqn_JSON_IteratorKeyIsNamedString_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_object_s *Dqn_JSON_IteratorKeyValueIsObj(Dqn_JSONIterator *it, Dqn_String8 key) { - json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(state); - json_string_s *result = state ? Dqn_JSON_ObjElementCheckNamedString_(curr, key, state->flags, false /*require*/, file, func, line) : nullptr; + json_object_s *result = nullptr; + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_object(curr->value); return result; } -json_number_s *Dqn_JSON_IteratorKeyIsNamedNumber_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_array_s *Dqn_JSON_IteratorKeyValueIsArray(Dqn_JSONIterator *it, Dqn_String8 key) { - json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(state); - json_number_s *result = state ? Dqn_JSON_ObjElementCheckNamedNumber_(curr, key, state->flags, false /*require*/, file, func, line) : nullptr; + json_array_s *result = nullptr; + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_array(curr->value); return result; } -json_value_s *Dqn_JSON_IteratorKeyIsNamedBool_(Dqn_JSONIterator *state, Dqn_String8 key, Dqn_String8 file, Dqn_String8 func, Dqn_uint line) +json_string_s *Dqn_JSON_IteratorKeyValueIsString(Dqn_JSONIterator *it, Dqn_String8 key) { - json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(state); - json_value_s *result = state ? Dqn_JSON_ObjElementCheckNamedBool_(curr, key, state->flags, false /*require*/, file, func, line) : nullptr; + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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_IteratorKeyValueIsNumber(Dqn_JSONIterator *it, Dqn_String8 key) +{ + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); + json_number_s *result = nullptr; + if (curr && Dqn_JSON_String8Cmp(curr->name, key)) + result = json_value_as_number(curr->value); + return result; +} -// NOTE: Dqn_JSON_IteratorKeyTo +json_value_s *Dqn_JSON_IteratorKeyValueIsBool(Dqn_JSONIterator *it, Dqn_String8 key) +{ + json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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; +} + +// NOTE: Dqn_JSON_IteratorValueTo // ----------------------------------------------------------------------------- -Dqn_String8 Dqn_JSON_IteratorKeyToString(Dqn_JSONIterator *state) +Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it) { Dqn_String8 result = {}; - if (json_string_s *curr = Dqn_JSON_IteratorKeyIsString(state)) + if (json_string_s *curr = Dqn_JSON_IteratorValueIsString(it)) result = Dqn_String8_Init(curr->string, curr->string_size); return result; } -int64_t Dqn_JSON_IteratorKeyToI64(Dqn_JSONIterator *state) +int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *it) { int64_t result = {}; - if (json_number_s *curr = Dqn_JSON_IteratorKeyIsNumber(state)) + if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it)) result = Dqn_String8_ToI64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/); return result; } -bool Dqn_JSON_IteratorKeyToBool(Dqn_JSONIterator *state) +uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it) +{ + uint64_t result = {}; + if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it)) + result = Dqn_String8_ToU64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/); + return result; +} + +bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it) { bool result = {}; - if (json_value_s *curr = Dqn_JSON_IteratorKeyIsBool(state)) + if (json_value_s *curr = Dqn_JSON_IteratorValueIsBool(it)) result = curr->type == json_type_true; return result; } + +void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_CallSite call_site) +{ + if (!it) + return; + + json_object_element_s const *curr = Dqn_JSON_IteratorCurrObjElement(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, + key->string_size, + key->string, + value_type_size, + value_type); + } else { + Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, + call_site, + "Unknown key-value pair in object [key=%.*s, value=%.*s]", + key->string_size, + key->string, + value_type_size, + value_type); + } +} + #endif // defined(DQN_JSON_IMPLEMENTATION) diff --git a/dqn_unit_tests.cpp b/dqn_unit_tests.cpp index 8acd870..caf89ac 100644 --- a/dqn_unit_tests.cpp +++ b/dqn_unit_tests.cpp @@ -2,19 +2,22 @@ // NOTE: Preprocessor Config // ------------------------------------------------------------------------------------------------- /* -#define DQN_TEST_WITH_MAIN Define this to enable the main function and allow standalone compiling - and running of the file. +#define DQN_TEST_WITH_MAIN Define this to enable the main function and allow standalone compiling + and running of the file. +#define DQN_TEST_WITH_KECCAK Define this to enable the main function and allow standalone compiling + and running of the file. */ #if defined(DQN_TEST_WITH_MAIN) #define DQN_IMPLEMENTATION #include "dqn.h" - - #define DQN_KECCAK_IMPLEMENTATION - #include "dqn_keccak.h" #endif -#include "dqn_tests_helpers.cpp" +#if defined(DQN_TEST_WITH_KECCAK) + #define DQN_KECCAK_IMPLEMENTATION + #include "dqn_keccak.h" + #include "dqn_tests_helpers.cpp" +#endif #define DQN_TESTER_IMPLEMENTATION #include "dqn_tester.h" @@ -145,7 +148,7 @@ Dqn_Tester TestFixedArray() DQN_TESTER_GROUP(test, "Dqn_FArray") { DQN_TESTER_TEST("Initialise from raw array") { int raw_array[] = {1, 2}; - auto array = Dqn_FArray_Init(raw_array, (int)Dqn_CArray_Count(raw_array)); + auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_ICOUNT(raw_array)); DQN_TESTER_ASSERT(&test, array.size == 2); DQN_TESTER_ASSERT(&test, array.data[0] == 1); DQN_TESTER_ASSERT(&test, array.data[1] == 2); @@ -153,7 +156,7 @@ Dqn_Tester TestFixedArray() DQN_TESTER_TEST("Erase stable 1 element from array") { int raw_array[] = {1, 2, 3}; - auto array = Dqn_FArray_Init(raw_array, (int)Dqn_CArray_Count(raw_array)); + auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_ICOUNT(raw_array)); Dqn_FArray_EraseStable(&array, 1); DQN_TESTER_ASSERT(&test, array.size == 2); DQN_TESTER_ASSERT(&test, array.data[0] == 1); @@ -162,7 +165,7 @@ Dqn_Tester TestFixedArray() DQN_TESTER_TEST("Erase unstable 1 element from array") { int raw_array[] = {1, 2, 3}; - auto array = Dqn_FArray_Init(raw_array, (int)Dqn_CArray_Count(raw_array)); + auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_ICOUNT(raw_array)); DQN_TESTER_ASSERT(&test, Dqn_FArray_EraseUnstable(&array, 0)); DQN_TESTER_ASSERT(&test, array.size == 2); DQN_TESTER_ASSERT(&test, array.data[0] == 3); @@ -172,7 +175,7 @@ Dqn_Tester TestFixedArray() DQN_TESTER_TEST("Add 1 element to array") { int const ITEM = 2; int raw_array[] = {1}; - auto array = Dqn_FArray_Init(raw_array, (int)Dqn_CArray_Count(raw_array)); + auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_ICOUNT(raw_array)); Dqn_FArray_Add(&array, ITEM); DQN_TESTER_ASSERT(&test, array.size == 2); DQN_TESTER_ASSERT(&test, array.data[0] == 1); @@ -181,7 +184,7 @@ Dqn_Tester TestFixedArray() DQN_TESTER_TEST("Clear array") { int raw_array[] = {1}; - auto array = Dqn_FArray_Init(raw_array, (int)Dqn_CArray_Count(raw_array)); + auto array = Dqn_FArray_Init(raw_array, DQN_ARRAY_ICOUNT(raw_array)); Dqn_FArray_Clear(&array, Dqn_ZeroMem_No); DQN_TESTER_ASSERT(&test, array.size == 0); } @@ -735,9 +738,9 @@ Dqn_Tester TestOS() DQN_TESTER_TEST("Generate secure RNG 32 bytes") { char const ZERO[32] = {}; char buf[32] = {}; - Dqn_b32 result = Dqn_OS_SecureRNGBytes(buf, Dqn_CArray_CountI(buf)); + bool result = Dqn_OS_SecureRNGBytes(buf, DQN_ARRAY_UCOUNT(buf)); DQN_TESTER_ASSERT(&test, result); - DQN_TESTER_ASSERT(&test, DQN_MEMCMP(buf, ZERO, Dqn_CArray_Count(buf)) != 0); + DQN_TESTER_ASSERT(&test, DQN_MEMCMP(buf, ZERO, DQN_ARRAY_UCOUNT(buf)) != 0); } DQN_TESTER_TEST("Generate secure RNG 0 bytes") { @@ -1236,7 +1239,7 @@ Dqn_Tester TestWin() wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0}; DQN_TESTER_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); - DQN_TESTER_ASSERTF(&test, size_returned == Dqn_CArray_Count(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, Dqn_CArray_Count(EXPECTED) - 1); + DQN_TESTER_ASSERTF(&test, size_returned == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, DQN_ARRAY_UCOUNT(EXPECTED) - 1); DQN_TESTER_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0); } @@ -1253,13 +1256,14 @@ Dqn_Tester TestWin() char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0}; DQN_TESTER_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned); - DQN_TESTER_ASSERTF(&test, size_returned == Dqn_CArray_Count(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, Dqn_CArray_Count(EXPECTED) - 1); + DQN_TESTER_ASSERTF(&test, size_returned == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, DQN_ARRAY_UCOUNT(EXPECTED) - 1); DQN_TESTER_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0); } } return test; } +#if defined(DQN_TEST_WITH_KECCAK) #define DQN_TESTER_HASH_X_MACRO \ DQN_TESTER_HASH_X_ENTRY(SHA3_224, "SHA3-224") \ DQN_TESTER_HASH_X_ENTRY(SHA3_256, "SHA3-256") \ @@ -1470,6 +1474,7 @@ Dqn_Tester TestKeccak() } return test; } +#endif // defined(DQN_TEST_WITH_KECCAK) void TestRunSuite() { @@ -1483,7 +1488,9 @@ void TestRunSuite() TestFixedArray(), TestHex(), TestIntrinsics(), + #if defined(DQN_TEST_WITH_KECCAK) TestKeccak(), + #endif TestM4(), TestOS(), TestPerfCounter(),