From b536f407b855a5cd68dd191bbe05404907cdb1ad Mon Sep 17 00:00:00 2001 From: doyle Date: Thu, 22 Jul 2021 19:59:27 +1000 Subject: [PATCH] test: Organize unit tests --- Code/Dqn.h | 544 ++++++++++++++------- Code/Dqn_Tests.cpp | 1156 +++++++++++++++++++++++--------------------- 2 files changed, 957 insertions(+), 743 deletions(-) diff --git a/Code/Dqn.h b/Code/Dqn.h index 7a4f437..4187a6a 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -1,74 +1,78 @@ // ------------------------------------------------------------------------------------------------- +// NOTE: Dqn +// ------------------------------------------------------------------------------------------------- +// General all-purpose utility library. // -// NOTE: Preprocessor Config +// ----------------------------------------------------------------------------- +// NOTE: Configuration +// ----------------------------------------------------------------------------- +// #define DQN_IMPLEMENTATION +// Define this in one and only one C++ file to enable the implementation +// code of the header file +// +// #define DQN_NO_ASSERT +// Turn all assertion macros to no-ops +// +// #define DQN_NO_WIN32_MINIMAL_HEADER +// Define this to stop this library from defining a minimal subset of Win32 +// prototypes and definitions in this file. Useful for stopping redefinition +// of symbols if another library includes "Windows.h" +// +// #define DQN_NO_WIN32_SHLWAPI_H +// See DQN_NO_WIN32_MINIMAL_HEADER. Useful if another library includes +// "shlwapi.h" +// +// #define DQN_STATIC_API +// Apply static to all function definitions and disable external linkage to +// other translation units. +// +// #define DQN_STB_SPRINTF_HEADER_ONLY +// Define this to stop this library from defining +// STB_SPRINTF_IMPLEMENTATION. Useful if another library uses and includes +// "stb_sprintf.h" +// +// #define DQN_MEMZERO_DEBUG_BYTE 0xDA +// By defining 'DQN_MEMZERO_DEBUG_BYTE' to some byte value this will enable +// functions that receive Dqn_ZeroMem::No to memset the non-zeroed memory to +// 'DQN_MEMZERO_DEBUG_BYTE' to help detect holding pointers to invalid memory, +// i.e. holding pointers to an array element that was popped from the array. +// +// #define DQN_ALLOCATION_TRACING 1 +// When defined to 0 all tracing code is compiled out. +// +// When defined to 1, some allocating calls in the library will automatically +// get passed in the file name, function name, line number and an optional tag. +// +// For data structures that have a 'Dqn_AllocationTracer' member, the caller +// can set the 'Dqn_AllocationTracer' to log allocations every time they are +// made in the data structure. +// +// 'Tagged' variants of functions accept as the last parameter, a 'tag' that is +// the descriptor/name to describe the allocation. All extra parameters and +// tags are compiled out when tracing is disabled. +// +// #define DQN_ALLOCATOR_DEFAULT_TO_NULL +// If defined, zero initialising an allocator uses the null allocator (i.e. +// crash on allocation). This forces the user to specify explicitly which +// allocator to use, for example. +// +// Dqn_Allocator allocator = {}; +// Dqn_Allocator_Allocate(allocator, ...) // <- This asserts and crashes +// +// So you have to explicitly do +// +// Dqn_Allocator allocator = {}; +// allocator.type = Dqn_AllocatorType::Heap; +// +// or +// +// Dqn_Allocator allocator = Dqn_AllocatorHeap() +// +// When not defined a zero initialised allocator will use the Heap +// Dqn_Allocator via malloc, realloc, free. // // ------------------------------------------------------------------------------------------------- -/* -#define DQN_IMPLEMENTATION Define this in one and only one C++ file to enable the - implementation code of the header file - -#define DQN_NO_ASSERT Turn all assertion macros to no-ops - -#define DQN_NO_WIN32_WINDOWS_H Define this to stop this library from defining a minimal subset - of Win32 prototypes and definitions in this file. Useful for - stopping redefinition of symbols if another library includes - "Windows.h" - -#define DQN_NO_WIN32_SHLWAPI_H See DQN_NO_WIN32_WINDOWS_H. Useful if another library includes - "shlwapi.h" - -#define DQN_STATIC_API Apply static to all function definitions and disable external - linkage to other translation units. - -#define DQN_STB_SPRINTF_HEADER_ONLY Define this to stop this library from defining - STB_SPRINTF_IMPLEMENTATION. Useful if another library uses and - includes "stb_sprintf.h" - -#define DQN_MEMZERO_DEBUG_BYTE 0xDA - By defining 'DQN_MEMZERO_DEBUG_BYTE' to some byte value this will enable - functions that receive Dqn_ZeroMem::No to memset the non-zeroed memory to - 'DQN_MEMZERO_DEBUG_BYTE' to help detect holding pointers to invalid memory, - i.e. holding pointers to an array element that was popped from the array. - -#define DQN_ALLOCATION_TRACING 1 - When defined to 0 all tracing code is compiled out. - - When defined to 1, some allocating calls in the library will automatically - get passed in the file name, function name, line number and an optional tag. - - For data structures that have a 'Dqn_AllocationTracer' member, the caller - can set the 'Dqn_AllocationTracer' to log allocations every time they are - made in the data structure. - - 'Tagged' variants of functions accept as the last parameter, a 'tag' that is - the descriptor/name to describe the allocation. All extra parameters and - tags are compiled out when tracing is disabled. - -#define DQN_ALLOCATOR_DEFAULT_TO_NULL - If defined, zero initialising an allocator uses the null allocator (i.e. - crash on allocation). This forces the user to specify explicitly which - allocator to use, for example. - - Dqn_Allocator allocator = {}; - Dqn_Allocator_Allocate(allocator, ...) // <- This asserts and crashes - - So you have to explicitly do - - Dqn_Allocator allocator = {}; - allocator.type = Dqn_AllocatorType::Heap; - - or - - Dqn_Allocator allocator = Dqn_AllocatorHeap() - - When not defined a zero initialised allocator will use the Heap - Dqn_Allocator via malloc, realloc, free. -*/ - -// ------------------------------------------------------------------------------------------------- -// // NOTE: Library Config -// // ------------------------------------------------------------------------------------------------- /* Dqn library logs errors and outputs through Dqn_Log(...). This is @@ -383,7 +387,7 @@ static_assert(sizeof(void *) == sizeof(Dqn_usize), "Require: Pointer can be held // // ------------------------------------------------------------------------------------------------ #if defined(DQN_OS_WIN32) - #if !defined(DQN_NO_WIN32_WINDOWS_H) + #if !defined(DQN_NO_WIN32_MINIMAL_HEADER) // Taken from Windows.h typedef unsigned long DWORD; typedef unsigned short WORD; @@ -515,12 +519,12 @@ void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex); void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex); // NOTE: Advance API, more granular functions, the basic sequence to use the API is -/* +#if 0 Dqn_TicketMutex mutex = {}; unsigned int ticket = Dqn_TicketMutex_MakeTicket(&mutex); Dqn_TicketMutex_BeginTicket(&mutex, ticket); // Blocking call until we attain the lock Dqn_TicketMutex_End(&mutex); - */ +#endif unsigned int Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex); void Dqn_TicketMutex_BeginTicket (const Dqn_TicketMutex *mutex, unsigned int ticket); Dqn_b32 Dqn_TicketMutex_CanLock (const Dqn_TicketMutex *mutex, unsigned int ticket); @@ -530,23 +534,29 @@ Dqn_b32 Dqn_TicketMutex_CanLock (const Dqn_TicketMutex *mutex, u // NOTE: stb_sprintf // // ------------------------------------------------------------------------------------------------- -// stb_sprintf - v1.05 - public domain snprintf() implementation +// stb_sprintf - v1.10 - public domain snprintf() implementation // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 // http://github.com/nothings/stb // // allowed types: sc uidBboXx p AaGgEef n -// lengths : h ll j z t I64 I32 I +// lengths : hh h ll j z t I64 I32 I // // Contributors: // Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) // // Contributors (bugfixes): // github:d26435 // github:trex78 +// github:account-login // Jari Komppa (SI suffixes) // Rohit Nirmal // Marcin Wojdyr // Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb // // LICENSE: // @@ -624,8 +634,6 @@ doesn't either). If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT and you'll save 4K of code space. - - 64-BIT INTS: ============ This library also supports 64-bit integers and you can use MSVC style or @@ -670,46 +678,77 @@ PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): "...512 char string..." ( 35.0x/32.5x faster!) */ -#if defined(__has_feature) - #if __has_feature(address_sanitizer) - #define STBI__ASAN __attribute__((no_sanitize("address"))) +#if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) #endif + #endif + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif #endif -#ifndef STBI__ASAN -#define STBI__ASAN + +#ifndef STBSP__ASAN +#define STBSP__ASAN #endif #ifdef STB_SPRINTF_STATIC #define STBSP__PUBLICDEC static -#define STBSP__PUBLICDEF static STBI__ASAN +#define STBSP__PUBLICDEF static STBSP__ASAN #else #ifdef __cplusplus #define STBSP__PUBLICDEC extern "C" -#define STBSP__PUBLICDEF extern "C" STBI__ASAN +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN #else #define STBSP__PUBLICDEC extern -#define STBSP__PUBLICDEF STBI__ASAN +#define STBSP__PUBLICDEF STBSP__ASAN #endif #endif -#include // for va_list() +#if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) + #endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t #ifndef STB_SPRINTF_MIN #define STB_SPRINTF_MIN 512 // how many characters per callback #endif -typedef char *STBSP_SPRINTFCB(char *buf, void *user, int len); +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); #ifndef STB_SPRINTF_DECORATE #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names #endif -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); #endif // STB_SPRINTF_H_INCLUDE // ------------------------------------------------------------------------------------------------- @@ -1134,7 +1173,7 @@ DQN_API Dqn_String Dqn_String__InitArenaFmt (Dqn_ArenaAllocator *arena D #define Dqn_String_InitArenaTaggedFmtV( arena, tag, fmt, ...) Dqn_String__InitArenaFmtV(arena DQN_CALL_SITE(tag), fmt, ## __VA_ARGS__) #define Dqn_String_InitArenaFmtV( arena, fmt, ...) Dqn_String__InitArenaFmtV(arena DQN_CALL_SITE(""), fmt, ## __VA_ARGS__) -DQN_API Dqn_String Dqn_String__InitArenaFmtV (Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS, char const *fmt, va_list va DQN_CALL_SITE_ARGS); +DQN_API Dqn_String Dqn_String__InitArenaFmtV (Dqn_ArenaAllocator *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String_Allocate (Dqn_Allocator *allocator, Dqn_isize size, Dqn_ZeroMem zero_mem); DQN_API Dqn_String Dqn_String_ArenaAllocate (Dqn_ArenaAllocator *arena, Dqn_isize size, Dqn_ZeroMem zero_mem); @@ -1142,7 +1181,11 @@ DQN_API Dqn_b32 Dqn_String_Compare (Dqn_String const lhs, Dqn_S DQN_API Dqn_b32 Dqn_String_CompareCaseInsensitive(Dqn_String const lhs, Dqn_String const rhs); // allocator: (Optional) When null, the string is allocated with DQN_MALLOC, result should be freed with DQN_FREE. -DQN_API Dqn_String Dqn_String_Copy (Dqn_String const src, Dqn_Allocator *allocator); +#define Dqn_String_Copy( src, allocator) Dqn_String__Copy(src, allocator DQN_CALL_SITE("")) +#define Dqn_String_ArenaCopy( src, arena) Dqn_String__ArenaCopy(src, arena DQN_CALL_SITE("")) + +DQN_API Dqn_String Dqn_String__Copy (Dqn_String const src, Dqn_Allocator *allocator DQN_CALL_SITE_ARGS); +DQN_API Dqn_String Dqn_String__ArenaCopy (Dqn_String const src, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String_TrimWhitespaceAround (Dqn_String src); DQN_API Dqn_b32 operator== (Dqn_String const &lhs, Dqn_String const &rhs); @@ -1156,6 +1199,9 @@ DQN_API void Dqn_String_Free (Dqn_String *string, Dqn_Allo DQN_API Dqn_b32 Dqn_String_StartsWith(Dqn_String string, Dqn_String prefix); DQN_API Dqn_Slice Dqn_String_Split (Dqn_String src, Dqn_Allocator *allocator); +DQN_API Dqn_u64 Dqn_String_ToU64 (Dqn_String str); +DQN_API Dqn_i64 Dqn_String_ToI64 (Dqn_String str); + // ------------------------------------------------------------------------------------------------- // @@ -1552,10 +1598,18 @@ DQN_API Dqn_u64 Dqn_Safe_MulU64 (Dqn_u64 a, Dqn_u64 b); DQN_API int Dqn_Safe_TruncateISizeToInt (Dqn_isize val); DQN_API Dqn_i32 Dqn_Safe_TruncateISizeToI32 (Dqn_isize val); DQN_API Dqn_i8 Dqn_Safe_TruncateISizeToI8 (Dqn_isize val); -DQN_API Dqn_u32 Dqn_Safe_TruncateUSizeToU32 (Dqn_u64 val); +DQN_API Dqn_u32 Dqn_Safe_TruncateUSizeToU32 (Dqn_usize val); DQN_API int Dqn_Safe_TruncateUSizeToI32 (Dqn_usize val); DQN_API int Dqn_Safe_TruncateUSizeToInt (Dqn_usize val); DQN_API Dqn_isize Dqn_Safe_TruncateUSizeToISize(Dqn_usize val); +DQN_API Dqn_u32 Dqn_Safe_TruncateU64ToU32 (Dqn_u64 val); +DQN_API Dqn_u16 Dqn_Safe_TruncateU64ToU16 (Dqn_u64 val); +DQN_API Dqn_u8 Dqn_Safe_TruncateU64ToU8 (Dqn_u64 val); +DQN_API Dqn_i64 Dqn_Safe_TruncateU64ToI64 (Dqn_u64 val); +DQN_API Dqn_i32 Dqn_Safe_TruncateU64ToI32 (Dqn_u64 val); +DQN_API Dqn_i16 Dqn_Safe_TruncateU64ToI16 (Dqn_u64 val); +DQN_API Dqn_i8 Dqn_Safe_TruncateU64ToI8 (Dqn_u64 val); +DQN_API int Dqn_Safe_TruncateU64ToInt (Dqn_u64 val); // ------------------------------------------------------------------------------------------------- // @@ -1865,6 +1919,7 @@ template struct Dqn_Array template DQN_API Dqn_Array Dqn_Array_InitWithMemory (T *memory, Dqn_isize max, Dqn_isize size = 0); #define Dqn_Array_InitWithAllocatorNoGrow(allocator, Type, max, size, zero_mem) Dqn_Array__InitWithAllocatorNoGrow(allocator, max, size, zero_mem DQN_CALL_SITE("")) +#define Dqn_Array_InitWithArenaNoGrow( arena, Type, max, size, zero_mem) Dqn_Array__InitWithArenaNoGrow( arena, max, size, zero_mem DQN_CALL_SITE("")) #define Dqn_Array_Reserve( array, size) Dqn_Array__Reserve(array, size DQN_CALL_SITE("")) template DQN_API void Dqn_Array_Free (Dqn_Array *a); @@ -2782,6 +2837,14 @@ DQN_API Dqn_Array Dqn_Array__InitWithAllocatorNoGrow(Dqn_Allocator *allocator return result; } +template +DQN_API Dqn_Array Dqn_Array__InitWithArenaNoGrow(Dqn_ArenaAllocator *arena, Dqn_isize max, Dqn_isize size, Dqn_ZeroMem zero_mem DQN_CALL_SITE_ARGS) +{ + T *memory = DQN_CAST(T *)Dqn_ArenaAllocator__Allocate(arena, sizeof(T) * max, alignof(T), zero_mem DQN_CALL_SITE_ARGS_INPUT); + Dqn_Array result = Dqn_Array_InitWithMemory(memory, max, size); + return result; +} + template DQN_API bool Dqn_Array__Reserve(Dqn_Array *a, Dqn_isize size DQN_CALL_SITE_ARGS) { @@ -3007,7 +3070,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) extern "C" int PathFileExistsA(char const *path); #endif // !defined(DQN_NO_WIN32_SHWLAPI_H) - #if !defined(DQN_NO_WIN32_WINDOWS_H) + #if !defined(DQN_NO_WIN32_MINIMAL_HEADER) #pragma comment(lib, "shlwapi.lib") // Taken from Windows.h @@ -3082,7 +3145,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) // // NOTE: MultiByteToWideChar // - #define CP_UTF8 65001 // UTF-8 translation + #define CP_UTF8 65001 // UTF-8 translation // // NOTE: FormatMessageA @@ -3110,8 +3173,6 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) // extern "C" { - long _InterlockedExchangeAdd (long volatile *addend, long value); - __int64 _InterlockedExchangeAdd64(__int64 volatile *addend, __int64 value); BOOL CopyFileA (char const *existing_file_name, char const *new_file_name, BOOL fail_if_exists); BOOL FreeLibrary (void *lib_module); BOOL QueryPerformanceCounter (LARGE_INTEGER *performance_count); @@ -3300,9 +3361,9 @@ DQN_API void Dqn_LogV(Dqn_LogType type, // NOTE: Use the callback version of stb_sprintf to allow us to chunk logs and print arbitrary // sized format strings without needing to size it up first. - auto stb_vsprintf_callback = [](char *buf, void *user, int len) -> char * { + auto stb_vsprintf_callback = [](char const *buf, void *user, int len) -> char * { fprintf(DQN_CAST(FILE *)user, "%.*s", len, buf); - return buf; + return DQN_CAST(char *)buf; }; char stb_buffer[STB_SPRINTF_MIN * 2] = {}; @@ -3723,14 +3784,14 @@ DQN_API Dqn_String Dqn_String__InitArenaFmt(Dqn_ArenaAllocator *arena DQN_CALL_S DQN_API Dqn_String Dqn_String__InitArenaFmtV(Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS, char const *fmt, va_list va DQN_CALL_SITE_ARGS) { Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(arena); - Dqn_String result = Dqn_String__InitFmtV(&allocator, fmt, va DQN_CALL_SITE_ARGS_INPUT); + Dqn_String result = Dqn_String__InitFmtV(&allocator, fmt, va DQN_CALL_SITE_ARGS_INPUT); return result; } DQN_API Dqn_String Dqn_String_Allocate(Dqn_Allocator *allocator, Dqn_isize size, Dqn_ZeroMem zero_mem) { Dqn_String result = {}; - result.str = Dqn_Allocator_NewArray(allocator, char, size, zero_mem); + result.str = Dqn_Allocator_NewArray(allocator, char, size + 1, zero_mem); result.cap = size; return result; } @@ -3738,7 +3799,7 @@ DQN_API Dqn_String Dqn_String_Allocate(Dqn_Allocator *allocator, Dqn_isize size, DQN_API Dqn_String Dqn_String_ArenaAllocate(Dqn_ArenaAllocator *arena, Dqn_isize size, Dqn_ZeroMem zero_mem) { Dqn_String result = {}; - result.str = Dqn_ArenaAllocator_NewArray(arena, char, size, zero_mem); + result.str = Dqn_ArenaAllocator_NewArray(arena, char, size + 1, zero_mem); result.cap = size; return result; } @@ -3759,13 +3820,23 @@ DQN_API Dqn_b32 Dqn_String_CompareCaseInsensitive(Dqn_String const lhs, Dqn_Stri return result; } -#define Dqn_String_Copy(src, allocator) Dqn_String__Copy(src, allocator DQN_CALL_SITE("")) +DQN_API Dqn_String Dqn_String__ArenaCopy(Dqn_String const src, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS) +{ + Dqn_String result = src; + result.str = DQN_CAST(char *)Dqn_ArenaAllocator_Allocate(arena, result.size + 1, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT); + result.cap = result.size; + DQN_MEMCOPY(result.str, src.str, DQN_CAST(size_t)result.size); + result.str[result.size] = 0; + return result; +} + DQN_API Dqn_String Dqn_String__Copy(Dqn_String const src, Dqn_Allocator *allocator DQN_CALL_SITE_ARGS) { Dqn_String result = src; - result.str = allocator ? DQN_CAST(char *)Dqn_Allocator__Allocate(allocator, result.size, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT) : - DQN_CAST(char *)DQN_MALLOC(result.size); - result.cap = result.size; + result.str = allocator ? DQN_CAST(char *)Dqn_Allocator__Allocate(allocator, result.size + 1, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT) : + DQN_CAST(char *)DQN_MALLOC(result.size + 1); + result.cap = result.size; + result.str[result.size] = 0; DQN_MEMCOPY(result.str, src.str, DQN_CAST(size_t)result.size); return result; } @@ -3877,6 +3948,18 @@ DQN_API Dqn_Slice Dqn_String_Split(Dqn_String src, Dqn_Allocator *al return result; } +DQN_API Dqn_u64 Dqn_String_ToU64(Dqn_String str) +{ + Dqn_u64 result = Dqn_Str_ToU64(str.str, DQN_CAST(int)str.size); + return result; +} + +DQN_API Dqn_i64 Dqn_String_ToI64(Dqn_String str) +{ + Dqn_i64 result = Dqn_Str_ToI64(str.str, DQN_CAST(int)str.size); + return result; +} + // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_ArenaAllocator @@ -4024,11 +4107,11 @@ DQN_API void Dqn_ArenaAllocator_ResetUsage(Dqn_ArenaAllocator *arena, Dqn_ZeroMe DQN_API Dqn_ArenaAllocatorRegion Dqn_ArenaAllocator_BeginRegion(Dqn_ArenaAllocator *arena) { - Dqn_ArenaAllocatorRegion result = {}; - result.arena = arena; - result.curr_mem_block = arena->curr_mem_block; - result.curr_mem_block_used = (arena->curr_mem_block) ? arena->curr_mem_block->used : 0; - result.top_mem_block = arena->top_mem_block; + Dqn_ArenaAllocatorRegion result = {}; + result.arena = arena; + result.curr_mem_block = arena->curr_mem_block; + result.curr_mem_block_used = (arena->curr_mem_block) ? arena->curr_mem_block->used : 0; + result.top_mem_block = arena->top_mem_block; return result; } @@ -4629,14 +4712,14 @@ DQN_API Dqn_b32 Dqn_Bit_IsNotSet(Dqn_u64 bits, Dqn_u64 bits_to_check) // ------------------------------------------------------------------------------------------------- DQN_API Dqn_i64 Dqn_Safe_AddI64(Dqn_i64 a, Dqn_i64 b) { - DQN_ASSERT_MSG(a <= DQN_I64_MAX - b, "%I64u <= %I64u", a, DQN_I64_MAX - b); + DQN_ASSERT_MSG(a <= DQN_I64_MAX - b, "%I64d <= %I64d", a, DQN_I64_MAX - b); Dqn_i64 result = (a <= DQN_I64_MAX - b) ? (a + b) : DQN_I64_MAX; return result; } DQN_API Dqn_i64 Dqn_Safe_MulI64(Dqn_i64 a, Dqn_i64 b) { - DQN_ASSERT_MSG(a <= DQN_I64_MAX / b , "%I64u <= %I64u", a, DQN_I64_MAX / b); + DQN_ASSERT_MSG(a <= DQN_I64_MAX / b , "%I64d <= %I64d", a, DQN_I64_MAX / b); Dqn_i64 result = (a <= DQN_I64_MAX / b) ? (a * b) : DQN_I64_MAX; return result; } @@ -4644,7 +4727,7 @@ DQN_API Dqn_i64 Dqn_Safe_MulI64(Dqn_i64 a, Dqn_i64 b) DQN_API Dqn_u64 Dqn_Safe_AddU64(Dqn_u64 a, Dqn_u64 b) { DQN_ASSERT_MSG(a <= DQN_U64_MAX - b, "%I64u <= %I64u", a, DQN_U64_MAX - b); - Dqn_u64 result = (a <= DQN_U64_MAX / b) ? (a + b) : DQN_U64_MAX; + Dqn_u64 result = (a <= DQN_U64_MAX - b) ? (a + b) : DQN_U64_MAX; return result; } @@ -4657,7 +4740,7 @@ DQN_API Dqn_u64 Dqn_Safe_SubU64(Dqn_u64 a, Dqn_u64 b) DQN_API Dqn_u32 Dqn_Safe_SubU32(Dqn_u32 a, Dqn_u32 b) { - DQN_ASSERT_MSG(a >= b, "%I64u >= %I64u", a, b); + DQN_ASSERT_MSG(a >= b, "%I32u >= %I32u", a, b); Dqn_u32 result = (a >= b) ? (a - b) : 0; return result; } @@ -4671,47 +4754,103 @@ DQN_API Dqn_u64 Dqn_Safe_MulU64(Dqn_u64 a, Dqn_u64 b) DQN_API int Dqn_Safe_TruncateISizeToInt(Dqn_isize val) { - DQN_ASSERT_MSG(val >= DQN_I32_MIN && val <= DQN_I32_MAX, "%I64d >= %I64d && %I64d <= %I64d", val, DQN_I32_MAX, val, DQN_I32_MAX); + DQN_ASSERT_MSG(val >= DQN_I32_MIN && val <= DQN_I32_MAX, "%zd >= %zd && %zd <= %zd", val, DQN_I32_MAX, val, DQN_I32_MAX); auto result = (val >= DQN_I32_MIN && val <= DQN_I32_MAX) ? DQN_CAST(int)val : 0; return result; } DQN_API Dqn_i32 Dqn_Safe_TruncateISizeToI32(Dqn_isize val) { - DQN_ASSERT_MSG(val >= DQN_I32_MIN && val <= DQN_I32_MAX, "%I64d >= %I64d && %I64d <= %I64d", val, DQN_I32_MIN, val, DQN_I32_MAX); + DQN_ASSERT_MSG(val >= DQN_I32_MIN && val <= DQN_I32_MAX, "%zd >= %zd && %zd <= %zd", val, DQN_I32_MIN, val, DQN_I32_MAX); auto result = (val >= DQN_I32_MIN && val <= DQN_I32_MAX) ? DQN_CAST(Dqn_i32)val : 0; return result; } -DQN_API Dqn_u32 Dqn_Safe_TruncateUSizeToU32(Dqn_u64 val) +DQN_API Dqn_u32 Dqn_Safe_TruncateUSizeToU32(Dqn_usize val) { - DQN_ASSERT_MSG(val <= DQN_U32_MAX, "%I64u <= %I64u", val, DQN_U32_MAX); + DQN_ASSERT_MSG(val <= DQN_U32_MAX, "%zu <= %zu", val, DQN_U32_MAX); auto result = (val <= DQN_U32_MAX) ? DQN_CAST(Dqn_u32)val : DQN_U32_MAX; return result; } - DQN_API Dqn_i32 Dqn_Safe_TruncateUSizeToI32(Dqn_usize val) { - DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%I64u <= %I64d", val, DQN_I32_MAX); + DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%zu <= %zu", val, DQN_I32_MAX); auto result = (val <= DQN_I32_MAX) ? DQN_CAST(int)val : DQN_I32_MAX; return result; } DQN_API int Dqn_Safe_TruncateUSizeToInt(Dqn_usize val) { - DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%I64u <= %I64d", val, DQN_I32_MAX); + DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%zu <= %zu", val, DQN_I32_MAX); auto result = (val <= DQN_I32_MAX) ? DQN_CAST(int)val : DQN_I32_MAX; return result; } DQN_API Dqn_isize Dqn_Safe_TruncateUSizeToISize(Dqn_usize val) { - DQN_ASSERT_MSG(val <= DQN_ISIZE_MAX, "%I64u <= %I64u", val, DQN_CAST(Dqn_usize)DQN_ISIZE_MAX); + DQN_ASSERT_MSG(val <= DQN_ISIZE_MAX, "%zu <= %zu", val, DQN_CAST(Dqn_usize)DQN_ISIZE_MAX); auto result = (val <= DQN_ISIZE_MAX) ? DQN_CAST(Dqn_isize)val : DQN_ISIZE_MAX; return result; } +DQN_API Dqn_u32 Dqn_Safe_TruncateU64ToU32(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_U32_MAX, "%I64u <= %I64u", val, DQN_U32_MAX); + auto result = (val <= DQN_U32_MAX) ? DQN_CAST(Dqn_u32)val : DQN_U32_MAX; + return result; +} + +DQN_API Dqn_u16 Dqn_Safe_TruncateU64ToU16(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_U16_MAX, "%I64u <= %I64u", val, DQN_U16_MAX); + auto result = (val <= DQN_U16_MAX) ? DQN_CAST(Dqn_u16)val : DQN_U16_MAX; + return result; +} + +DQN_API Dqn_u8 Dqn_Safe_TruncateU64ToU8(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_U8_MAX, "%I64u <= %I64u", val, DQN_U8_MAX); + auto result = (val <= DQN_U8_MAX) ? DQN_CAST(Dqn_u8)val : DQN_U8_MAX; + return result; +} + +DQN_API Dqn_i64 Dqn_Safe_TruncateU64ToI64(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_I64_MAX, "%I64u <= %I64d", val, DQN_I64_MAX); + auto result = (val <= DQN_I64_MAX) ? DQN_CAST(Dqn_i64)val : DQN_I64_MAX; + return result; +} + + +DQN_API Dqn_i32 Dqn_Safe_TruncateU64ToI32(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%I64u <= %I64d", val, DQN_I32_MAX); + auto result = (val <= DQN_I32_MAX) ? DQN_CAST(Dqn_i32)val : DQN_I32_MAX; + return result; +} + +DQN_API Dqn_i16 Dqn_Safe_TruncateU64ToI16(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_I16_MAX, "%I64u <= %I64d", val, DQN_I16_MAX); + auto result = (val <= DQN_I16_MAX) ? DQN_CAST(Dqn_i16)val : DQN_I16_MAX; + return result; +} + +DQN_API Dqn_i8 Dqn_Safe_TruncateU64ToI8(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_I8_MAX, "%I64u <= %I64d", val, DQN_I8_MAX); + auto result = (val <= DQN_I8_MAX) ? DQN_CAST(Dqn_i8)val : DQN_I8_MAX; + return result; +} + +DQN_API int Dqn_Safe_TruncateU64ToInt(Dqn_u64 val) +{ + DQN_ASSERT_MSG(val <= DQN_I32_MAX, "%I64u <= %I64d", val, DQN_I32_MAX); + auto result = (val <= DQN_I32_MAX) ? DQN_CAST(int)val : DQN_I32_MAX; + return result; +} + // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_Char @@ -5697,7 +5836,6 @@ DQN_API Dqn_MurmurHash3_128 Dqn_MurmurHash3_x64_128(void const *key, int len, Dq #endif // DQN_IMPLEMENTATION #ifdef STB_SPRINTF_IMPLEMENTATION -#include // for va_arg() #define stbsp__uint32 unsigned int #define stbsp__int32 signed int @@ -5712,7 +5850,7 @@ DQN_API Dqn_MurmurHash3_128 Dqn_MurmurHash3_x64_128(void const *key, int len, Dq #define stbsp__uint16 unsigned short #ifndef stbsp__uintptr -#if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) #define stbsp__uintptr stbsp__uint64 #else #define stbsp__uintptr stbsp__uint32 @@ -5740,9 +5878,18 @@ static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, static char stbsp__period = '.'; static char stbsp__comma = ','; -static char stbsp__digitpair[201] = - "0001020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576" - "7778798081828384858687888990919293949596979899"; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { @@ -5779,6 +5926,46 @@ static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) } } +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { static char hex[] = "0123456789abcdefxp"; @@ -5848,7 +6035,17 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, if (callback) if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) goto schk1; - *(stbsp__uint32 *)bf = v; + #ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else + #endif + { + *(stbsp__uint32 *)bf = v; + } bf += 4; f += 4; } @@ -5950,9 +6147,12 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, case 'h': fl |= STBSP__HALFWIDTH; ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH break; // are we 64-bit (unix style) case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); ++f; if (f[0] == 'l') { fl |= STBSP__INTMAX; @@ -5961,13 +6161,16 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, break; // are we 64-bit on intmax? (c99) case 'j': - fl |= STBSP__INTMAX; + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; ++f; break; // are we 64-bit on size_t or ptrdiff_t? (c99) case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; case 't': - fl |= ((sizeof(char *) == 8) ? STBSP__INTMAX : 0); + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; ++f; break; // are we 64-bit (msft style) @@ -6006,37 +6209,9 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, s = va_arg(va, char *); if (s == 0) s = (char *)"null"; - // get the length - sn = s; - for (;;) { - if ((((stbsp__uintptr)sn) & 3) == 0) - break; - lchk: - if (sn[0] == 0) - goto ld; - ++sn; - } - n = 0xffffffff; - if (pr >= 0) { - n = (stbsp__uint32)(sn - s); - if (n >= (stbsp__uint32)pr) - goto ld; - n = ((stbsp__uint32)(pr - n)) >> 2; - } - while (n) { - stbsp__uint32 v = *(stbsp__uint32 *)sn; - if ((v - 0x01010101) & (~v) & 0x80808080UL) - goto lchk; - sn += 4; - --n; - } - goto lchk; - ld: - - l = (stbsp__uint32)(sn - s); - // clamp to precision - if (l > (stbsp__uint32)pr) - l = pr; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); lead[0] = 0; tail[0] = 0; pr = 0; @@ -6077,8 +6252,8 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, lead[0] = 0; tail[0] = 0; pr = 0; - dp = 0; cs = 0; + STBSP__NOTUSED(dp); goto scopy; #else case 'A': // hex float @@ -6182,11 +6357,11 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, --pr; // when using %e, there is one digit before the decimal goto doexpfromg; } - // this is the insane action to get the pr to match %g sematics for %f + // this is the insane action to get the pr to match %g semantics for %f if (dp > 0) { pr = (dp < (stbsp__int32)l) ? l - dp : 0; } else { - pr = -dp + ((pr > (stbsp__int32)l) ? l : pr); + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); } goto dofloatfromg; @@ -6473,7 +6648,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, lead[0] = 0; if (pr == 0) { l = 0; - cs = (((l >> 4) & 15)) << 24; + cs = 0; goto scopy; } } @@ -6546,7 +6721,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, if ((fl & STBSP__TRIPLET_COMMA) == 0) { do { s -= 2; - *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair[(n % 100) * 2]; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; } while (n); } @@ -6701,7 +6876,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, stbsp__cb_buf_clamp(i, n); n -= i; STBSP__UNALIGNED(while (i >= 4) { - *(stbsp__uint32 *)bf = *(stbsp__uint32 *)s; + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; bf += 4; s += 4; i -= 4; @@ -6831,19 +7006,22 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, . typedef struct stbsp__context { char *buf; int count; + int length; char tmp[STB_SPRINTF_MIN]; } stbsp__context; -static char *stbsp__clamp_callback(char *buf, void *user, int len) +static char *stbsp__clamp_callback(const char *buf, void *user, int len) { stbsp__context *c = (stbsp__context *)user; + c->length += len; if (len > c->count) len = c->count; if (len) { if (buf != c->buf) { - char *s, *d, *se; + const char *s, *se; + char *d; d = c->buf; s = buf; se = buf + len; @@ -6856,38 +7034,36 @@ static char *stbsp__clamp_callback(char *buf, void *user, int len) } if (c->count <= 0) - return 0; + return c->tmp; return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can } -static char * stbsp__count_clamp_callback( char * buf, void * user, int len ) +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) { - (void)buf; stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); - c->count += len; + c->length += len; return c->tmp; // go direct into buffer if you can } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) { stbsp__context c; - int l; if ( (count == 0) && !buf ) { - c.count = 0; + c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); - l = c.count; } else { - if ( count == 0 ) - return 0; + int l; c.buf = buf; c.count = count; + c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); @@ -6898,7 +7074,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, c buf[l] = 0; } - return l; + return c.length; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) @@ -6945,7 +7121,7 @@ static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, *bits = b & ((((stbsp__uint64)1) << 52) - 1); *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); - return (stbsp__int32)(b >> 63); + return (stbsp__int32)((stbsp__uint64) b >> 63); } static double const stbsp__bot[23] = { @@ -7059,7 +7235,7 @@ static stbsp__uint64 const stbsp__powten[20] = { #define stbsp__ddtoS64(ob, xh, xl) \ { \ double ahi = 0, alo, vh, t; \ - ob = (stbsp__int64)ph; \ + ob = (stbsp__int64)xh; \ vh = (double)ob; \ ahi = (xh - vh); \ t = (ahi - xh); \ @@ -7155,7 +7331,7 @@ static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, c d = value; STBSP__COPYFP(bits, d); expo = (stbsp__int32)((bits >> 52) & 2047); - ng = (stbsp__int32)(bits >> 63); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); if (ng) d = -d; @@ -7169,7 +7345,7 @@ static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, c if (expo == 0) // is zero or denormal { - if ((bits << 1) == 0) // do zero + if (((stbsp__uint64) bits << 1) == 0) // do zero { *decimal_pos = 1; *start = out; @@ -7265,7 +7441,7 @@ static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, c } while (n) { out -= 2; - *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair[(n % 100) * 2]; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; e += 2; } diff --git a/Code/Dqn_Tests.cpp b/Code/Dqn_Tests.cpp index 7b1c40d..4b77767 100644 --- a/Code/Dqn_Tests.cpp +++ b/Code/Dqn_Tests.cpp @@ -136,600 +136,470 @@ void Dqn_TestState_PrintResult(Dqn_TestState const *result) } } -static void Dqn_Test_UnitTests() +void Dqn_Test_Allocator() { Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_Allocator - // --------------------------------------------------------------------------------------------- + // NOTE: Various allocator test { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); - - // NOTE: Various allocator test { - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } - - { - DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); - Dqn_ArenaAllocator arena = Dqn_ArenaAllocator_InitWithNewAllocator(Dqn_Allocator_InitWithHeap(), 0, nullptr); - Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } + DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); + char const EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } - // NOTE: Alignment Test { - Dqn_u8 const ALIGNMENT3 = 4; - Dqn_u8 const NUM_BYTES = sizeof(Dqn_u32); - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Align to 32 bytes"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Align to 32 bytes"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } - - { - DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Align to 32 bytes"); - Dqn_ArenaAllocator arena = {}; - if (arena.backup_allocator.type == Dqn_AllocatorType::Null) - arena.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } + DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); + char const EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } - // NOTE: Dqn_PointerMetadata tests { - Dqn_u8 const ALIGNMENT3 = 4; - Dqn_u8 const NUM_BYTES = 4; - Dqn_u8 const MAX_OFFSET = (ALIGNMENT3 - 1) + sizeof(Dqn_PointerMetadata); - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocation metadata initialised"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); - DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocation metadata initialised"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); - DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); - } + DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); + Dqn_ArenaAllocator arena = Dqn_ArenaAllocator_InitWithNewAllocator(Dqn_Allocator_InitWithHeap(), 0, nullptr); + Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); + char const EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } } - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_Array - // --------------------------------------------------------------------------------------------- + // NOTE: Alignment Test { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); - // NOTE: Dqn_Array_InitWithMemory + Dqn_u8 const ALIGNMENT3 = 4; + Dqn_u8 const NUM_BYTES = sizeof(Dqn_u32); { - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more"); - int memory[4] = {}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - Dqn_Array_Add(&array, 1); - Dqn_Array_Add(&array, 2); - Dqn_Array_Add(&array, 3); - Dqn_Array_Add(&array, 4); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data %d", array.data[3]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - - int *added_item = Dqn_Array_Add(&array, 5); - DQN_TEST_EXPECT(testing_state, added_item == nullptr); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items"); - int memory[4] = {}; - int DATA[] = {1, 2, 3}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed"); - int memory[4] = {}; - int DATA[] = {1, 2, 3}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); - Dqn_Array_Clear(&array, Dqn_ZeroMem::No); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d. Clear but don't zero memory so old values should still remain", array.data[0]); - - Dqn_Array_Clear(&array, Dqn_ZeroMem::Yes); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 0, "array.data %d. Clear but zero memory old values should not remain", array.data[0]); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable"); - int memory[4] = {}; - int DATA[] = {1, 2, 3, 4}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); - Dqn_Array_EraseUnstable(&array, 1); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 4, "array.data %d", array.data[1]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); - - Dqn_Array_EraseStable(&array, 0); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 4, "array.data: %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 3, "array.data: %d", array.data[1]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 2, "array.size: %d", array.size); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek"); - int memory[4] = {}; - int DATA[] = {1, 2, 3}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); - Dqn_Array_Pop(&array, 2); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - - int *peek_item = Dqn_Array_Peek(&array); - DQN_TEST_EXPECT_MSG(testing_state, *peek_item == 1, "peek: %d", *peek_item); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing"); - int memory[4] = {}; - Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); - DQN_DEFER { Dqn_Array_Free(&array); }; - } + DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Align to 32 bytes"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); + auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); } - // NOTE: Dynamic Memory: Dqn_Array { - { - DQN_TEST_START_SCOPE(testing_state, "Dynamic Memory: Reserve and check over commit reallocates"); - Dqn_Array array = {}; - if (array.allocator.type == Dqn_AllocatorType::Null) - array.allocator = Dqn_Allocator_InitWithHeap(); + DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Align to 32 bytes"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); + auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); + } - DQN_DEFER { Dqn_Array_Free(&array); }; + { + DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Align to 32 bytes"); + Dqn_ArenaAllocator arena = {}; + if (arena.backup_allocator.type == Dqn_AllocatorType::Null) + arena.backup_allocator = Dqn_Allocator_InitWithHeap(); - Dqn_Array_Reserve(&array, 4); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - - int DATA[] = {1, 2, 3, 4}; - Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); - DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data: %d", array.data[1]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data: %d", array.data[2]); - DQN_TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data: %d", array.data[3]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - - int *added_item = Dqn_Array_Add(&array, 5); - DQN_TEST_EXPECT_MSG(testing_state, *added_item == 5, "added_item: %d", *added_item); - DQN_TEST_EXPECT_MSG(testing_state, array.data[4] == 5, "array.data: %d", array.data[4]); - DQN_TEST_EXPECT_MSG(testing_state, array.size == 5, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, array.max >= 5, "array.max: %d", array.max); - } + Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); + auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); + int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); } } - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_Rect - // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_PointerMetadata tests { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Rect"); - // NOTE: Dqn_Rect_Intersection + Dqn_u8 const ALIGNMENT3 = 4; + Dqn_u8 const NUM_BYTES = 4; + Dqn_u8 const MAX_OFFSET = (ALIGNMENT3 - 1) + sizeof(Dqn_PointerMetadata); { - { - DQN_TEST_START_SCOPE(testing_state, "No intersection"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(200, 0), Dqn_V2(200, 200)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocation metadata initialised"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); + DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); + DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); + } - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 0 && ab.max.y == 0, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + { + DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocation metadata initialised"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); + DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; + Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); + DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); + DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); + } + } +} - { - DQN_TEST_START_SCOPE(testing_state, "A's min intersects B"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); +void Dqn_Test_Array() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); + // NOTE: Dqn_Array_InitWithMemory + { + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more"); + int memory[4] = {}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + Dqn_Array_Add(&array, 1); + Dqn_Array_Add(&array, 2); + Dqn_Array_Add(&array, 3); + Dqn_Array_Add(&array, 4); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data %d", array.data[3]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + int *added_item = Dqn_Array_Add(&array, 5); + DQN_TEST_EXPECT(testing_state, added_item == nullptr); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } - { - DQN_TEST_START_SCOPE(testing_state, "B's min intersects A"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); + Dqn_Array_Clear(&array, Dqn_ZeroMem::No); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d. Clear but don't zero memory so old values should still remain", array.data[0]); - { - DQN_TEST_START_SCOPE(testing_state, "A's max intersects B"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + Dqn_Array_Clear(&array, Dqn_ZeroMem::Yes); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 0, "array.data %d. Clear but zero memory old values should not remain", array.data[0]); + } - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable"); + int memory[4] = {}; + int DATA[] = {1, 2, 3, 4}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); + Dqn_Array_EraseUnstable(&array, 1); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 4, "array.data %d", array.data[1]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); - { - DQN_TEST_START_SCOPE(testing_state, "B's max intersects A"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + Dqn_Array_EraseStable(&array, 0); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 4, "array.data: %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 3, "array.data: %d", array.data[1]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 2, "array.size: %d", array.size); + } - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); + Dqn_Array_Pop(&array, 2); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + int *peek_item = Dqn_Array_Peek(&array); + DQN_TEST_EXPECT_MSG(testing_state, *peek_item == 1, "peek: %d", *peek_item); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } - { - DQN_TEST_START_SCOPE(testing_state, "B contains A"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); - - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } - - { - DQN_TEST_START_SCOPE(testing_state, "A contains B"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); - Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25)); - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); - - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } - - { - DQN_TEST_START_SCOPE(testing_state, "A equals B"); - Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100)); - Dqn_Rect b = a; - Dqn_Rect ab = Dqn_Rect_Intersection(a, b); - - DQN_TEST_EXPECT_MSG(testing_state, - ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 100 && ab.max.y == 100, - "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", - ab.min.x, - ab.min.y, - ab.max.x, - ab.max.y); - } + { + DQN_TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing"); + int memory[4] = {}; + Dqn_Array array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*size*/); + DQN_DEFER { Dqn_Array_Free(&array); }; } } - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_StringBuilder - // --------------------------------------------------------------------------------------------- + // NOTE: Dynamic Memory: Dqn_Array { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - // NOTE: Dqn_StringBuilder_Append { - { - DQN_TEST_START_SCOPE(testing_state, "Append variable size strings and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + DQN_TEST_START_SCOPE(testing_state, "Dynamic Memory: Reserve and check over commit reallocates"); + Dqn_Array array = {}; + if (array.allocator.type == Dqn_AllocatorType::Null) + array.allocator = Dqn_Allocator_InitWithHeap(); - Dqn_StringBuilder_Append(&builder, "Abc", 1); - Dqn_StringBuilder_Append(&builder, "cd"); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + DQN_DEFER { Dqn_Array_Free(&array); }; - char const EXPECT_STR[] = "Acd"; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } + Dqn_Array_Reserve(&array, 4); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - { - DQN_TEST_START_SCOPE(testing_state, "Append empty string and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + int DATA[] = {1, 2, 3, 4}; + Dqn_Array_AddArray(&array, DATA, Dqn_ArrayCount(DATA)); + DQN_TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data: %d", array.data[1]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data: %d", array.data[2]); + DQN_TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data: %d", array.data[3]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - Dqn_StringBuilder_Append(&builder, ""); - Dqn_StringBuilder_Append(&builder, ""); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = ""; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Append empty string onto string and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_Append(&builder, "Acd"); - Dqn_StringBuilder_Append(&builder, ""); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = "Acd"; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Append nullptr and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_Append(&builder, nullptr, 5); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = ""; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using heap allocator"); - Dqn_StringBuilder<2> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_Append(&builder, "A"); - Dqn_StringBuilder_Append(&builder, "z"); // Should force a new memory block - Dqn_StringBuilder_Append(&builder, "tec"); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = "Aztec"; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - } - - // NOTE: Dqn_StringBuilder_AppendChar - { - DQN_TEST_START_SCOPE(testing_state, "Append char and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_AppendChar(&builder, 'a'); - Dqn_StringBuilder_AppendChar(&builder, 'b'); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = "ab"; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - - // NOTE: Dqn_StringBuilder_AppendFmt - { - { - DQN_TEST_START_SCOPE(testing_state, "Append format string and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_AppendFmt(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor"); - Dqn_StringBuilder_AppendFmt(&builder, "Extra Stuff"); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff"; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } - - { - DQN_TEST_START_SCOPE(testing_state, "Append nullptr format string and build using heap allocator"); - Dqn_StringBuilder<> builder = {}; - if (builder.backup_allocator.type == Dqn_AllocatorType::Null) - builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_StringBuilder_AppendFmt(&builder, nullptr); - Dqn_isize size = 0; - char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); - DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - - char const EXPECT_STR[] = ""; - DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); - } + int *added_item = Dqn_Array_Add(&array, 5); + DQN_TEST_EXPECT_MSG(testing_state, *added_item == 5, "added_item: %d", *added_item); + DQN_TEST_EXPECT_MSG(testing_state, array.data[4] == 5, "array.data: %d", array.data[4]); + DQN_TEST_EXPECT_MSG(testing_state, array.size == 5, "array.size: %d", array.size); + DQN_TEST_EXPECT_MSG(testing_state, array.max >= 5, "array.max: %d", array.max); } } +} - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_FixedArray - // --------------------------------------------------------------------------------------------- +void Dqn_Test_FixedArray() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedArray"); + // NOTE: Dqn_FixedArray_Init { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedArray"); - // NOTE: Dqn_FixedArray_Init - { - DQN_TEST_START_SCOPE(testing_state, "Initialise from raw array"); - int raw_array[] = {1, 2}; - auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); - DQN_TEST_EXPECT(testing_state, array.size == 2); - DQN_TEST_EXPECT(testing_state, array[0] == 1); - DQN_TEST_EXPECT(testing_state, array[1] == 2); - } - - // NOTE: Dqn_FixedArray_EraseStable - { - DQN_TEST_START_SCOPE(testing_state, "Erase stable 1 element from array"); - int raw_array[] = {1, 2, 3}; - auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); - Dqn_FixedArray_EraseStable(&array, 1); - DQN_TEST_EXPECT(testing_state, array.size == 2); - DQN_TEST_EXPECT(testing_state, array[0] == 1); - DQN_TEST_EXPECT(testing_state, array[1] == 3); - } - - // NOTE: Dqn_FixedArray_EraseUnstable - { - DQN_TEST_START_SCOPE(testing_state, "Erase unstable 1 element from array"); - int raw_array[] = {1, 2, 3}; - auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); - Dqn_FixedArray_EraseUnstable(&array, 0); - DQN_TEST_EXPECT(testing_state, array.size == 2); - DQN_TEST_EXPECT(testing_state, array[0] == 3); - DQN_TEST_EXPECT(testing_state, array[1] == 2); - } - - // NOTE: Dqn_FixedArray_Add - { - DQN_TEST_START_SCOPE(testing_state, "Add 1 element to array"); - int const ITEM = 2; - int raw_array[] = {1}; - auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); - Dqn_FixedArray_Add(&array, ITEM); - DQN_TEST_EXPECT(testing_state, array.size == 2); - DQN_TEST_EXPECT(testing_state, array[0] == 1); - DQN_TEST_EXPECT(testing_state, array[1] == ITEM); - } - - // NOTE: Dqn_FixedArray_Clear - { - DQN_TEST_START_SCOPE(testing_state, "Clear array"); - int raw_array[] = {1}; - auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); - Dqn_FixedArray_Clear(&array); - DQN_TEST_EXPECT(testing_state, array.size == 0); - } + DQN_TEST_START_SCOPE(testing_state, "Initialise from raw array"); + int raw_array[] = {1, 2}; + auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); + DQN_TEST_EXPECT(testing_state, array.size == 2); + DQN_TEST_EXPECT(testing_state, array[0] == 1); + DQN_TEST_EXPECT(testing_state, array[1] == 2); } - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_FixedString - // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_FixedArray_EraseStable { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedString"); - - // NOTE: Dqn_FixedString_Append - { - DQN_TEST_START_SCOPE(testing_state, "Append too much fails"); - Dqn_FixedString<4> str = {}; - DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_Append(&str, "abcd") == false, "We need space for the null-terminator"); - } - - // NOTE: Dqn_FixedString_AppendFmt - { - DQN_TEST_START_SCOPE(testing_state, "Append format string too much fails"); - Dqn_FixedString<4> str = {}; - DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_AppendFmt(&str, "abcd") == false, "We need space for the null-terminator"); - } + DQN_TEST_START_SCOPE(testing_state, "Erase stable 1 element from array"); + int raw_array[] = {1, 2, 3}; + auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); + Dqn_FixedArray_EraseStable(&array, 1); + DQN_TEST_EXPECT(testing_state, array.size == 2); + DQN_TEST_EXPECT(testing_state, array[0] == 1); + DQN_TEST_EXPECT(testing_state, array[1] == 3); } + // NOTE: Dqn_FixedArray_EraseUnstable + { + DQN_TEST_START_SCOPE(testing_state, "Erase unstable 1 element from array"); + int raw_array[] = {1, 2, 3}; + auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); + Dqn_FixedArray_EraseUnstable(&array, 0); + DQN_TEST_EXPECT(testing_state, array.size == 2); + DQN_TEST_EXPECT(testing_state, array[0] == 3); + DQN_TEST_EXPECT(testing_state, array[1] == 2); + } + + // NOTE: Dqn_FixedArray_Add + { + DQN_TEST_START_SCOPE(testing_state, "Add 1 element to array"); + int const ITEM = 2; + int raw_array[] = {1}; + auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); + Dqn_FixedArray_Add(&array, ITEM); + DQN_TEST_EXPECT(testing_state, array.size == 2); + DQN_TEST_EXPECT(testing_state, array[0] == 1); + DQN_TEST_EXPECT(testing_state, array[1] == ITEM); + } + + // NOTE: Dqn_FixedArray_Clear + { + DQN_TEST_START_SCOPE(testing_state, "Clear array"); + int raw_array[] = {1}; + auto array = Dqn_FixedArray_Init(raw_array, (int)Dqn_ArrayCount(raw_array)); + Dqn_FixedArray_Clear(&array); + DQN_TEST_EXPECT(testing_state, array.size == 0); + } +} + +void Dqn_Test_FixedString() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedString"); + + // NOTE: Dqn_FixedString_Append + { + DQN_TEST_START_SCOPE(testing_state, "Append too much fails"); + Dqn_FixedString<4> str = {}; + DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_Append(&str, "abcd") == false, "We need space for the null-terminator"); + } + + // NOTE: Dqn_FixedString_AppendFmt + { + DQN_TEST_START_SCOPE(testing_state, "Append format string too much fails"); + Dqn_FixedString<4> str = {}; + DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_AppendFmt(&str, "abcd") == false, "We need space for the null-terminator"); + } +} + +void Dqn_Test_M4() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_M4"); + { + DQN_TEST_START_SCOPE(testing_state, "Simple translate and scale matrix"); + Dqn_M4 translate = Dqn_M4_TranslateF(1, 2, 3); + Dqn_M4 scale = Dqn_M4_ScaleF(2, 2, 2); + Dqn_M4 result = Dqn_M4_Mul(translate, scale); + + const Dqn_M4 EXPECT = {{ + {2, 0, 0, 0}, + {0, 2, 0, 0}, + {0, 0, 2, 0}, + {1, 2, 3, 1}, + }}; + + DQN_TEST_EXPECT_MSG(testing_state, + memcmp(result.columns, EXPECT.columns, sizeof(EXPECT)) == 0, + "\nresult =\n%s\nexpected =\n%s", + Dqn_M4_ColumnMajorString(result).str, + Dqn_M4_ColumnMajorString(EXPECT).str); + } +} + +void Dqn_Test_Rect() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Rect"); + // NOTE: Dqn_Rect_Intersection + { + { + DQN_TEST_START_SCOPE(testing_state, "No intersection"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(200, 0), Dqn_V2(200, 200)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 0 && ab.max.y == 0, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "A's min intersects B"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "B's min intersects A"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "A's max intersects B"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "B's max intersects A"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + + { + DQN_TEST_START_SCOPE(testing_state, "B contains A"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "A contains B"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100)); + Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25)); + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + + { + DQN_TEST_START_SCOPE(testing_state, "A equals B"); + Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100)); + Dqn_Rect b = a; + Dqn_Rect ab = Dqn_Rect_Intersection(a, b); + + DQN_TEST_EXPECT_MSG(testing_state, + ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 100 && ab.max.y == 100, + "ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }", + ab.min.x, + ab.min.y, + ab.max.x, + ab.max.y); + } + } +} + +void Dqn_Test_Str() +{ + Dqn_TestingState testing_state = {}; // --------------------------------------------------------------------------------------------- // NOTE: Dqn_Str_ToI64 // --------------------------------------------------------------------------------------------- @@ -872,39 +742,207 @@ static void Dqn_Test_UnitTests() DQN_TEST_EXPECT(testing_state, result[0] == '6' && result[1] == 'a'); } } +} - // --------------------------------------------------------------------------------------------- - // NOTE: Dqn_M4 - // --------------------------------------------------------------------------------------------- +void Dqn_Test_StringBuilder() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder"); + Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); + // NOTE: Dqn_StringBuilder_Append { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_M4"); { - DQN_TEST_START_SCOPE(testing_state, "Simple translate and scale matrix"); - Dqn_M4 translate = Dqn_M4_TranslateF(1, 2, 3); - Dqn_M4 scale = Dqn_M4_ScaleF(2, 2, 2); - Dqn_M4 result = Dqn_M4_Mul(translate, scale); + DQN_TEST_START_SCOPE(testing_state, "Append variable size strings and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); - const Dqn_M4 EXPECT = {{ - {2, 0, 0, 0}, - {0, 2, 0, 0}, - {0, 0, 2, 0}, - {1, 2, 3, 1}, - }}; + Dqn_StringBuilder_Append(&builder, "Abc", 1); + Dqn_StringBuilder_Append(&builder, "cd"); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - DQN_TEST_EXPECT_MSG(testing_state, - memcmp(result.columns, EXPECT.columns, sizeof(EXPECT)) == 0, - "\nresult =\n%s\nexpected =\n%s", - Dqn_M4_ColumnMajorString(result).str, - Dqn_M4_ColumnMajorString(EXPECT).str); + char const EXPECT_STR[] = "Acd"; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Append empty string and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_Append(&builder, ""); + Dqn_StringBuilder_Append(&builder, ""); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = ""; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Append empty string onto string and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_Append(&builder, "Acd"); + Dqn_StringBuilder_Append(&builder, ""); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = "Acd"; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Append nullptr and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_Append(&builder, nullptr, 5); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = ""; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using heap allocator"); + Dqn_StringBuilder<2> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_Append(&builder, "A"); + Dqn_StringBuilder_Append(&builder, "z"); // Should force a new memory block + Dqn_StringBuilder_Append(&builder, "tec"); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = "Aztec"; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); } } + + // NOTE: Dqn_StringBuilder_AppendChar + { + DQN_TEST_START_SCOPE(testing_state, "Append char and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_AppendChar(&builder, 'a'); + Dqn_StringBuilder_AppendChar(&builder, 'b'); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = "ab"; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + // NOTE: Dqn_StringBuilder_AppendFmt + { + { + DQN_TEST_START_SCOPE(testing_state, "Append format string and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_AppendFmt(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor"); + Dqn_StringBuilder_AppendFmt(&builder, "Extra Stuff"); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff"; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Append nullptr format string and build using heap allocator"); + Dqn_StringBuilder<> builder = {}; + if (builder.backup_allocator.type == Dqn_AllocatorType::Null) + builder.backup_allocator = Dqn_Allocator_InitWithHeap(); + + Dqn_StringBuilder_AppendFmt(&builder, nullptr); + Dqn_isize size = 0; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; + + char const EXPECT_STR[] = ""; + DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); + DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result); + } + } +} + +void Dqn_Test_TicketMutex() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_TicketMutex"); + { + // TODO: We don't have a meaningful test but since atomics are + // implemented with a macro this ensures that we test that they are + // written correctly. + DQN_TEST_START_SCOPE(testing_state, "Ticket mutex start and stop"); + Dqn_TicketMutex mutex = {}; + Dqn_TicketMutex_Begin(&mutex); + Dqn_TicketMutex_End(&mutex); + DQN_TEST_EXPECT(testing_state, mutex.ticket == mutex.serving + 1); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Ticket mutex start and stop w/ advanced API"); + Dqn_TicketMutex mutex = {}; + unsigned int ticket_a = Dqn_TicketMutex_MakeTicket(&mutex); + unsigned int ticket_b = Dqn_TicketMutex_MakeTicket(&mutex); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)Dqn_TicketMutex_CanLock(&mutex, ticket_b) == false); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)Dqn_TicketMutex_CanLock(&mutex, ticket_a) == true); + + Dqn_TicketMutex_BeginTicket(&mutex, ticket_a); + Dqn_TicketMutex_End(&mutex); + Dqn_TicketMutex_BeginTicket(&mutex, ticket_b); + Dqn_TicketMutex_End(&mutex); + + DQN_TEST_EXPECT(testing_state, mutex.ticket == mutex.serving + 1); + DQN_TEST_EXPECT(testing_state, mutex.ticket == ticket_b + 1); + } +} + +void Dqn_Test_RunSuite() +{ + Dqn_Test_Allocator(); + Dqn_Test_Array(); + Dqn_Test_FixedArray(); + Dqn_Test_FixedString(); + Dqn_Test_M4(); + Dqn_Test_Rect(); + Dqn_Test_Str(); + Dqn_Test_StringBuilder(); + Dqn_Test_TicketMutex(); } #if defined(DQN_TEST_WITH_MAIN) int main(int argc, char *argv[]) { (void)argv; (void)argc; - Dqn_Test_UnitTests(); + Dqn_Test_RunSuite(); return 0; } #endif