diff --git a/Code/Build.bat b/Code/Build.bat index 97d90bd..94a7761 100644 --- a/Code/Build.bat +++ b/Code/Build.bat @@ -11,5 +11,5 @@ REM Oi Use CPU Intrinsics REM Z7 Combine multi-debug files to one debug file REM wd4201 Nonstandard extension used: nameless struct/union REM Tp Treat header file as CPP source file -cl /MT /EHa /GR- /Od /Oi /Z7 /W4 /WX /wd4201 ../Code/Dqn_UnitTests.cpp /link /nologo +cl /MT /EHa /GR- /Od /Oi /Z7 /W4 /WX /wd4201 /D DQN_TEST_WITH_MAIN ../Code/Dqn_Tests.cpp /link /nologo popd diff --git a/Code/Dqn.h b/Code/Dqn.h index f49a8e2..7a4f437 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -120,10 +120,15 @@ // NOTE: Compiler // // ------------------------------------------------------------------------------------------------- +// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER #if defined(_MSC_VER) - #define DQN_COMPILER_MSVC - #pragma warning(push) - #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union + #if defined(__clang__) + #define DQN_COMPILER_W32_CLANG + #else + #define DQN_COMPILER_W32_MSVC + #pragma warning(push) + #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union + #endif #elif defined(__clang__) #define DQN_COMPILER_CLANG #elif defined(__GNUC__) @@ -134,7 +139,7 @@ #define DQN_OS_WIN32 #endif -#if defined(DQN_COMPILER_MSVC) +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #if defined(_CRT_SECURE_NO_WARNINGS) #define DQN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED #else @@ -195,10 +200,6 @@ // // ------------------------------------------------------------------------------------------------- #define DQN_CAST(val) (val) -#define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val)) -#define DQN_SQUARED(val) ((val) * (val)) -#define DQN_MIN(a, b) ((a < b) ? (a) : (b)) -#define DQN_MAX(a, b) ((a > b) ? (a) : (b)) #define DQN_SWAP(a, b) \ do \ { \ @@ -207,41 +208,38 @@ b = tmp; \ } while (0) -#define DQN_LEN_AND_STR(string) Dqn_CharCount(str), string -#define DQN_STR_AND_LEN(string) string, Dqn_CharCount(string) -#define DQN_STR_AND_LEN_I(string) string, (int)Dqn_CharCount(string) -#define DQN_FOR_EACH(i, limit) for (Dqn_isize i = 0; i < (Dqn_isize)(limit); ++i) -#define DQN_FOR_EACH_REVERSE(i, limit) for (Dqn_isize i = (Dqn_isize)(limit-1); i >= 0; --i) -#define DQN_FOR_EACH_ITERATOR(it_name, array, num) for (auto it_name = array; it_name != (array + num); it_name++) +#define DQN_LEN_AND_STR(string) Dqn_CharCount(str), string +#define DQN_STR_AND_LEN(string) string, Dqn_CharCount(string) +#define DQN_STR_AND_LEN_I(string) string, (int)Dqn_CharCount(string) +#define DQN_FOR_EACH(i, limit) for (Dqn_isize i = 0; i < (Dqn_isize)(limit); ++i) -#define DQN_BYTES(val) (val) +#define DQN_BYTES(val) (val) #define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val)) #define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val)) #define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val)) -#define DQN_MINS_TO_S(val) ((val) * 60ULL) +#define DQN_MINS_TO_S(val) ((val) * 60ULL) #define DQN_HOURS_TO_S(val) (DQN_MINS_TO_S(val) * 60ULL) -#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL) -#define DQN_YEARS_TO_S(val) (DQN_DAYS_TO_S(val) * 365ULL) +#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL) +#define DQN_YEARS_TO_S(val) (DQN_DAYS_TO_S(val) * 365ULL) #define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val) #if !defined(DQN_DEBUG_BREAK) #if defined(NDEBUG) #define DQN_DEBUG_BREAK #else - #if defined(DQN_COMPILER_MSVC) + #if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #define DQN_DEBUG_BREAK __debugbreak() #elif defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC) #include #define DQN_DEBUG_BREAK raise(SIGTRAP) + #elif + #error "Unhandled compiler" #endif #endif #endif -#define DQN_INVALID_CODE_PATH 0 #define DQN_SECONDS_TO_MS(val) ((val) * 1000.0f) -#define DQN_MATH_PI 3.14159265359f -#define DQN_DEGREE_TO_RADIAN(val) (val) * (DQN_MATH_PI / 180.0f) #if defined(DQN_STATIC_API) #define DQN_API static @@ -251,31 +249,65 @@ #define DQN_LOCAL_PERSIST static #define DQN_FILE_SCOPE static +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Overridable Math Macros +// +// ------------------------------------------------------------------------------------------------ +#ifndef DQN_M_SINF + #include + #define DQN_M_SINF(val) sinf(val) +#endif // DQN_M_SINF + +#ifndef DQN_M_COSF + #include + #define DQN_M_COSF(val) cosf(val) +#endif // DQN_M_COSF + +#ifndef DQN_M_TANF + #include + #define DQN_M_TANF(val) tanf(val) +#endif // DQN_M_TANF + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Math Macros +// +// ------------------------------------------------------------------------------------------------ +#define DQN_M_PI 3.14159265359f + +#define DQN_M_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_M_PI / 180.0f)) +#define DQN_M_RADIAN_TO_DEGREE(radians) ((radians) * (180.f * DQN_M_PI)) + +#define DQN_M_ABS(val) (((val) < 0) ? (-(val)) : (val)) +#define DQN_M_MAX(a, b) ((a > b) ? (a) : (b)) +#define DQN_M_MIN(a, b) ((a < b) ? (a) : (b)) +#define DQN_M_SQUARED(val) ((val) * (val)) + // ------------------------------------------------------------------------------------------------- // // NOTE: Assert Macro // // ------------------------------------------------------------------------------------------------ +#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERT_MSG(expr, "") +#define DQN_HARD_ASSERT_MSG(expr, fmt, ...) \ + if (!(expr)) \ + { \ + DQN_LOG_E("Assert: [" #expr "] " fmt, ##__VA_ARGS__); \ + DQN_DEBUG_BREAK; \ + } + #if defined(DQN_NO_ASSERT) #define DQN_ASSERT(expr) #define DQN_ASSERT_MSG(expr, fmt, ...) - #define DQN_ASSERT_IF(expr) if ((expr)) - #define DQN_ASSERT_MSG_IF(expr, fmt, ...) if ((expr)) #else - #define DQN_ASSERT(expr) DQN_ASSERT_MSG(expr, "") - #define DQN_ASSERT_MSG(expr, fmt, ...) \ - if (!(expr)) \ - { \ - DQN_LOG_E("Assert: [" #expr "] " fmt, ##__VA_ARGS__); \ - DQN_DEBUG_BREAK; \ - } - - #define DQN_ASSERT_IF(expr) DQN_ASSERT_MSG_IF(expr, "") - #define DQN_ASSERT_MSG_IF(expr, fmt, ...) \ - DQN_ASSERT_MSG(!(expr), fmt, ## __VA_ARGS__) \ - if (0) + #define DQN_ASSERT(expr) DQN_HARD_ASSERT_MSG(expr, "") + #define DQN_ASSERT_MSG(expr, fmt, ...) DQN_HARD_ASSERT_MSG(expr, fmt, ##__VA_ARGS__) #endif +#define DQN_INVALID_CODE_PATH DQN_ASSERT(0) +#define DQN_HARD_INVALID_CODE_PATH DQN_HARD_ASSERT(0 && "Invalid Code Path") + // ------------------------------------------------------------------------------------------------ // // NOTE: Typedefs @@ -290,7 +322,7 @@ using Dqn_i16 = signed short; using Dqn_u16 = unsigned short; using Dqn_i32 = signed int; using Dqn_u32 = unsigned int; -#if defined(DQN_COMPILER_MSVC) +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) using Dqn_i64 = signed __int64; using Dqn_u64 = unsigned __int64; #else @@ -434,19 +466,25 @@ DQN_API void Dqn__ZeroMemBytes(void *ptr, Dqn_usize count, Dqn_ZeroMem zero_mem) // NOTE: Intrinsics // // ------------------------------------------------------------------------------------------------- -#if defined(DQN_COMPILER_MSVC) +// NOTE: Dqn_AtomicAdd/Exchange return the previous value store in the target +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #include #define Dqn_AtomicAddU32(target, value) _InterlockedExchangeAdd(DQN_CAST(long volatile *)target, value) #define Dqn_AtomicAddU64(target, value) _InterlockedExchangeAdd64(DQN_CAST(__int64 volatile *)target, value) - #define Dqn_AtomicSetPointer(target, value) _InterlockedExchangePointer(DQN_CAST(void *volatile *)target, value) - #define Dqn_AtomicSetValue64(target, value) _InterlockedExchange64(DQN_CAST(__int64 volatile *)target, value) - #define Dqn_AtomicSetValue32(target, value) _InterlockedExchange(DQN_CAST(long volatile *)target, value) + #define Dqn_AtomicSubU32(target, value) InterlockedExchangeSubtract(DQN_CAST(unsigned long volatile *)target, value) + #define Dqn_AtomicSubU64(target, value) Dqn_AtomicAddU64(target, -value) + #define Dqn_AtomicSetPointer(target, value) InterlockedExchangePointer(DQN_CAST(void *volatile *)target, value) + #define Dqn_AtomicSetValue64(target, value) InterlockedExchange64(DQN_CAST(__int64 volatile *)target, value) + #define Dqn_AtomicSetValue32(target, value) InterlockedExchange(DQN_CAST(unsigned long volatile *)target, value) #define Dqn_CPUClockCycle() __rdtsc() - #define Dqn_WriteBarrierAndFence _WriteBarrier(); _mm_sfence() + #define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence() + #define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence() #elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) #include #define Dqn_AtomicAddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) #define Dqn_AtomicAddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL) + #define Dqn_AtomicSubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL) + #define Dqn_AtomicSubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL) #define Dqn_AtomicSetValue64(target, value) __sync_lock_test_and_set(target, value) #define Dqn_AtomicSetValue32(target, value) __sync_lock_test_and_set(target, value) #if defined(DQN_COMPILER_GCC) @@ -454,21 +492,15 @@ DQN_API void Dqn__ZeroMemBytes(void *ptr, Dqn_usize count, Dqn_ZeroMem zero_mem) #else #define Dqn_CPUClockCycle() __builtin_readcyclecounter() #endif - #define Dqn_WriteBarrierAndFence asm volatile("" ::: "memory") + #define Dqn_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory") + #define Dqn_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory") #else #error "Compiler not supported" #endif -union Dqn_CPUIDRegisters +struct Dqn_CPUIDRegisters { - unsigned int array[4]; - struct Register - { - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - }; + unsigned int array[4]; // eax, ebx, ecx, edx }; struct Dqn_TicketMutex @@ -478,9 +510,20 @@ struct Dqn_TicketMutex }; // Query the CPU's CPUID function and return the data in the registers -Dqn_CPUIDRegisters Dqn_CPUID (int function_id); -void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex); -void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex); +Dqn_CPUIDRegisters Dqn_CPUID (int function_id); +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 +/* + 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); + */ +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); // ------------------------------------------------------------------------------------------------- // @@ -689,11 +732,12 @@ DQN_API Dqn_isize Dqn_FmtLen (char const *fmt, ...); // // ------------------------------------------------------------------------------------------------- #define X_MACRO \ - X_ENTRY(Debug, "DEBUG") \ - X_ENTRY(Error, "ERROR") \ - X_ENTRY(Warning, "WARNING") \ - X_ENTRY(Info, "INFO") \ - X_ENTRY(Memory, "MEMORY") + X_ENTRY(Debug, "DBUG") \ + X_ENTRY(Error, "ERR ") \ + X_ENTRY(Warning, "WARN") \ + X_ENTRY(Info, "INFO") \ + X_ENTRY(Profile, "PROF") \ + X_ENTRY(Memory, "MEM ") enum struct Dqn_LogType { @@ -723,6 +767,7 @@ typedef void Dqn_LogProc(Dqn_LogType type, void *user_data, char const *file, Dq #define DQN_LOG_W(fmt, ...) dqn__lib.LogCallback(Dqn_LogType::Warning, dqn__lib.log_user_data, DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__, fmt, ## __VA_ARGS__) #define DQN_LOG_I(fmt, ...) dqn__lib.LogCallback(Dqn_LogType::Info, dqn__lib.log_user_data, DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__, fmt, ## __VA_ARGS__) #define DQN_LOG_M(fmt, ...) dqn__lib.LogCallback(Dqn_LogType::Memory, dqn__lib.log_user_data, DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__, fmt, ## __VA_ARGS__) +#define DQN_LOG_P(fmt, ...) dqn__lib.LogCallback(Dqn_LogType::Profile, dqn__lib.log_user_data, DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__, fmt, ## __VA_ARGS__) #define DQN_LOG(log_type, fmt, ...) dqn__lib.LogCallback(log_type, dqn__lib.log_user_data, DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__, fmt, ## __VA_ARGS__) // Update the default logging function, all logging functions will run through this callback @@ -731,8 +776,8 @@ typedef void Dqn_LogProc(Dqn_LogType type, void *user_data, char const *file, Dq DQN_API void Dqn_Log_SetCallback (Dqn_LogProc *proc, void *user_data); // Internal logging functions, prefer the logging macros above -DQN_API void Dqn_LogV (Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, va_list va); -DQN_API void Dqn_Log (Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, ...); +DQN_API void Dqn_LogV (Dqn_LogType type, void *user_data, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, va_list va); +DQN_API void Dqn_Log (Dqn_LogType type, void *user_data, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, ...); // ------------------------------------------------------------------------------------------------ // @@ -741,7 +786,7 @@ DQN_API void Dqn_Log (Dqn_LogType type, char const *file, Dqn_usize f // ------------------------------------------------------------------------------------------------ struct Dqn_Lib { - Dqn_LogProc *LogCallback; + Dqn_LogProc *LogCallback = Dqn_Log; void * log_user_data; #if defined(DQN_OS_WIN32) LARGE_INTEGER win32_qpc_frequency; @@ -786,203 +831,6 @@ DQN_API char *Dqn_PointerMetadata_Init (void *ptr, Dqn_isi DQN_API Dqn_PointerMetadata Dqn_PointerMetadata_Get (void *ptr); DQN_API char *Dqn_PointerMetadata_GetRawPointer(void *ptr); -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Math -// -// ------------------------------------------------------------------------------------------------- -union Dqn_V2I -{ - struct { Dqn_i32 x, y; }; - struct { Dqn_i32 w, h; }; - struct { Dqn_i32 min, max; }; - Dqn_i32 e[2]; - - Dqn_V2I() = default; - constexpr Dqn_V2I(Dqn_f32 x_, Dqn_f32 y_): x((Dqn_i32)x_), y((Dqn_i32)y_) {} - constexpr Dqn_V2I(Dqn_i32 x_, Dqn_i32 y_): x(x_), y(y_) {} - constexpr Dqn_V2I(Dqn_i32 xy): x(xy), y(xy) {} - - constexpr bool operator!=(Dqn_V2I other) const { return !(*this == other); } - constexpr bool operator==(Dqn_V2I other) const { return (x == other.x) && (y == other.y); } - constexpr bool operator>=(Dqn_V2I other) const { return (x >= other.x) && (y >= other.y); } - constexpr bool operator<=(Dqn_V2I other) const { return (x <= other.x) && (y <= other.y); } - constexpr bool operator< (Dqn_V2I other) const { return (x < other.x) && (y < other.y); } - constexpr bool operator> (Dqn_V2I other) const { return (x > other.x) && (y > other.y); } - constexpr Dqn_V2I operator- (Dqn_V2I other) const { Dqn_V2I result(x - other.x, y - other.y); return result; } - constexpr Dqn_V2I operator+ (Dqn_V2I other) const { Dqn_V2I result(x + other.x, y + other.y); return result; } - constexpr Dqn_V2I operator* (Dqn_V2I other) const { Dqn_V2I result(x * other.x, y * other.y); return result; } - constexpr Dqn_V2I operator* (Dqn_f32 other) const { Dqn_V2I result(x * other, y * other); return result; } - constexpr Dqn_V2I operator* (Dqn_i32 other) const { Dqn_V2I result(x * other, y * other); return result; } - constexpr Dqn_V2I operator/ (Dqn_V2I other) const { Dqn_V2I result(x / other.x, y / other.y); return result; } - constexpr Dqn_V2I operator/ (Dqn_f32 other) const { Dqn_V2I result(x / other, y / other); return result; } - constexpr Dqn_V2I operator/ (Dqn_i32 other) const { Dqn_V2I result(x / other, y / other); return result; } - constexpr Dqn_V2I &operator*=(Dqn_V2I other) { *this = *this * other; return *this; } - constexpr Dqn_V2I &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } - constexpr Dqn_V2I &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } - constexpr Dqn_V2I &operator-=(Dqn_V2I other) { *this = *this - other; return *this; } - constexpr Dqn_V2I &operator+=(Dqn_V2I other) { *this = *this + other; return *this; } -}; - -union Dqn_V2 -{ - struct { Dqn_f32 x, y; }; - struct { Dqn_f32 w, h; }; - struct { Dqn_f32 min, max; }; - Dqn_f32 e[2]; - - Dqn_V2() = default; - constexpr Dqn_V2(Dqn_f32 a) : x(a), y(a) {} - constexpr Dqn_V2(Dqn_i32 a) : x((Dqn_f32)a), y((Dqn_f32)a) {} - constexpr Dqn_V2(Dqn_f32 x, Dqn_f32 y): x(x), y(y) {} - constexpr Dqn_V2(Dqn_i32 x, Dqn_i32 y): x((Dqn_f32)x), y((Dqn_f32)y) {} - constexpr Dqn_V2(Dqn_V2I a) : x((Dqn_f32)a.x),y((Dqn_f32)a.y){} - - constexpr bool operator!=(Dqn_V2 other) const { return !(*this == other); } - constexpr bool operator==(Dqn_V2 other) const { return (x == other.x) && (y == other.y); } - constexpr bool operator>=(Dqn_V2 other) const { return (x >= other.x) && (y >= other.y); } - constexpr bool operator<=(Dqn_V2 other) const { return (x <= other.x) && (y <= other.y); } - constexpr bool operator< (Dqn_V2 other) const { return (x < other.x) && (y < other.y); } - constexpr bool operator> (Dqn_V2 other) const { return (x > other.x) && (y > other.y); } - constexpr Dqn_V2 operator- (Dqn_V2 other) const { Dqn_V2 result(x - other.x, y - other.y); return result; } - constexpr Dqn_V2 operator+ (Dqn_V2 other) const { Dqn_V2 result(x + other.x, y + other.y); return result; } - constexpr Dqn_V2 operator* (Dqn_V2 other) const { Dqn_V2 result(x * other.x, y * other.y); return result; } - constexpr Dqn_V2 operator* (Dqn_f32 other) const { Dqn_V2 result(x * other, y * other); return result; } - constexpr Dqn_V2 operator* (Dqn_i32 other) const { Dqn_V2 result(x * other, y * other); return result; } - constexpr Dqn_V2 operator/ (Dqn_V2 other) const { Dqn_V2 result(x / other.x, y / other.y); return result; } - constexpr Dqn_V2 operator/ (Dqn_f32 other) const { Dqn_V2 result(x / other, y / other); return result; } - constexpr Dqn_V2 operator/ (Dqn_i32 other) const { Dqn_V2 result(x / other, y / other); return result; } - constexpr Dqn_V2 &operator*=(Dqn_V2 other) { *this = *this * other; return *this; } - constexpr Dqn_V2 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } - constexpr Dqn_V2 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } - constexpr Dqn_V2 &operator/=(Dqn_V2 other) { *this = *this / other; return *this; } - constexpr Dqn_V2 &operator/=(Dqn_f32 other) { *this = *this / other; return *this; } - constexpr Dqn_V2 &operator/=(Dqn_i32 other) { *this = *this / other; return *this; } - constexpr Dqn_V2 &operator-=(Dqn_V2 other) { *this = *this - other; return *this; } - constexpr Dqn_V2 &operator+=(Dqn_V2 other) { *this = *this + other; return *this; } -}; - -DQN_API Dqn_V2I Dqn_V2_ToV2I (Dqn_V2 a); -DQN_API Dqn_V2 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a); -DQN_API Dqn_f32 Dqn_V2_Dot (Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_f32 Dqn_V2_LengthSq (Dqn_V2 a, Dqn_V2 b); -DQN_API Dqn_V2 Dqn_V2_Normalise (Dqn_V2 a); -DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a); - -union Dqn_V3 -{ - struct { Dqn_f32 x, y, z; }; - struct { Dqn_f32 r, g, b; }; - Dqn_V2 xy; - Dqn_f32 e[3]; - - Dqn_V3() = default; - constexpr Dqn_V3(Dqn_f32 a) : x(a), y(a), z(a) {} - constexpr Dqn_V3(Dqn_i32 a) : x((Dqn_f32)a), y((Dqn_f32)a), z((Dqn_f32)a) {} - constexpr Dqn_V3(Dqn_f32 x_, Dqn_f32 y_, Dqn_f32 z_): x(x_), y(y_), z(z_) {} - constexpr Dqn_V3(Dqn_i32 x_, Dqn_i32 y_, Dqn_f32 z_): x((Dqn_f32)x_), y((Dqn_f32)y_), z((Dqn_f32)z_) {} - constexpr Dqn_V3(Dqn_V2 xy, Dqn_f32 z_) : x(xy.x), y(xy.y), z(z_) {} - - constexpr bool operator!= (Dqn_V3 other) const { return !(*this == other); } - constexpr bool operator== (Dqn_V3 other) const { return (x == other.x) && (y == other.y) && (z == other.z); } - constexpr bool operator>= (Dqn_V3 other) const { return (x >= other.x) && (y >= other.y) && (z >= other.z); } - constexpr bool operator<= (Dqn_V3 other) const { return (x <= other.x) && (y <= other.y) && (z <= other.z); } - constexpr bool operator< (Dqn_V3 other) const { return (x < other.x) && (y < other.y) && (z < other.z); } - constexpr bool operator> (Dqn_V3 other) const { return (x > other.x) && (y > other.y) && (z > other.z); } - constexpr Dqn_V3 operator- (Dqn_V3 other) const { Dqn_V3 result(x - other.x, y - other.y, z - other.z); return result; } - constexpr Dqn_V3 operator+ (Dqn_V3 other) const { Dqn_V3 result(x + other.x, y + other.y, z + other.z); return result; } - constexpr Dqn_V3 operator* (Dqn_V3 other) const { Dqn_V3 result(x * other.x, y * other.y, z * other.z); return result; } - constexpr Dqn_V3 operator* (Dqn_f32 other) const { Dqn_V3 result(x * other, y * other, z * other); return result; } - constexpr Dqn_V3 operator* (Dqn_i32 other) const { Dqn_V3 result(x * other, y * other, z * other); return result; } - constexpr Dqn_V3 operator/ (Dqn_V3 other) const { Dqn_V3 result(x / other.x, y / other.y, z / other.z); return result; } - constexpr Dqn_V3 operator/ (Dqn_f32 other) const { Dqn_V3 result(x / other, y / other, z / other); return result; } - constexpr Dqn_V3 operator/ (Dqn_i32 other) const { Dqn_V3 result(x / other, y / other, z / other); return result; } - constexpr Dqn_V3 &operator*=(Dqn_V3 other) { *this = *this * other; return *this; } - constexpr Dqn_V3 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } - constexpr Dqn_V3 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } - constexpr Dqn_V3 &operator/=(Dqn_V3 other) { *this = *this / other; return *this; } - constexpr Dqn_V3 &operator/=(Dqn_f32 other) { *this = *this / other; return *this; } - constexpr Dqn_V3 &operator/=(Dqn_i32 other) { *this = *this / other; return *this; } - constexpr Dqn_V3 &operator-=(Dqn_V3 other) { *this = *this - other; return *this; } - constexpr Dqn_V3 &operator+=(Dqn_V3 other) { *this = *this + other; return *this; } -}; - -union Dqn_V4 -{ - struct { Dqn_f32 x, y, z, w; }; - struct { Dqn_f32 r, g, b, a; }; - struct { Dqn_V2 min; Dqn_V2 max; } v2; - Dqn_V3 rgb; - Dqn_f32 e[4]; - - Dqn_V4() = default; - constexpr Dqn_V4(Dqn_f32 xyzw) : x(xyzw), y(xyzw), z(xyzw), w(xyzw) {} - constexpr Dqn_V4(Dqn_f32 x_, Dqn_f32 y_, Dqn_f32 z_, Dqn_f32 w_): x(x_), y(y_), z(z_), w(w_) {} - constexpr Dqn_V4(Dqn_i32 x_, Dqn_i32 y_, Dqn_i32 z_, Dqn_i32 w_): x((Dqn_f32)x_), y((Dqn_f32)y_), z((Dqn_f32)z_), w((Dqn_f32)w_) {} - constexpr Dqn_V4(Dqn_V3 xyz, Dqn_f32 w_) : x(xyz.x), y(xyz.y), z(xyz.z), w(w_) {} - constexpr Dqn_V4(Dqn_V2 v2) : x(v2.x), y(v2.y), z(v2.x), w(v2.y) {} - - constexpr bool operator!=(Dqn_V4 other) const { return !(*this == other); } - constexpr bool operator==(Dqn_V4 other) const { return (x == other.x) && (y == other.y) && (z == other.z) && (w == other.w); } - constexpr bool operator>=(Dqn_V4 other) const { return (x >= other.x) && (y >= other.y) && (z >= other.z) && (w >= other.w); } - constexpr bool operator<=(Dqn_V4 other) const { return (x <= other.x) && (y <= other.y) && (z <= other.z) && (w <= other.w); } - constexpr bool operator< (Dqn_V4 other) const { return (x < other.x) && (y < other.y) && (z < other.z) && (w < other.w); } - constexpr bool operator> (Dqn_V4 other) const { return (x > other.x) && (y > other.y) && (z > other.z) && (w > other.w); } - constexpr Dqn_V4 operator- (Dqn_V4 other) const { Dqn_V4 result(x - other.x, y - other.y, z - other.z, w - other.w); return result; } - constexpr Dqn_V4 operator+ (Dqn_V4 other) const { Dqn_V4 result(x + other.x, y + other.y, z + other.z, w + other.w); return result; } - constexpr Dqn_V4 operator* (Dqn_V4 other) const { Dqn_V4 result(x * other.x, y * other.y, z * other.z, w * other.w); return result; } - constexpr Dqn_V4 operator* (Dqn_f32 other) const { Dqn_V4 result(x * other, y * other, z * other, w * other); return result; } - constexpr Dqn_V4 operator* (Dqn_i32 other) const { Dqn_V4 result(x * other, y * other, z * other, w * other); return result; } - constexpr Dqn_V4 operator/ (Dqn_f32 other) const { Dqn_V4 result(x / other, y / other, z / other, w / other); return result; } - constexpr Dqn_V4 &operator*=(Dqn_V4 other) { *this = *this * other; return *this; } - constexpr Dqn_V4 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } - constexpr Dqn_V4 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } - constexpr Dqn_V4 &operator-=(Dqn_V4 other) { *this = *this - other; return *this; } - constexpr Dqn_V4 &operator+=(Dqn_V4 other) { *this = *this + other; return *this; } -}; - -DQN_API Dqn_f32 Dqn_V4_Dot(Dqn_V4 const *a, Dqn_V4 const *b); - -struct Dqn_Rect -{ - Dqn_V2 min, max; - Dqn_Rect() = default; - Dqn_Rect(Dqn_V2 min, Dqn_V2 max) : min(min), max(max) {} - Dqn_Rect(Dqn_V2I min, Dqn_V2I max) : min(min), max(max) {} - Dqn_Rect(Dqn_f32 x, Dqn_f32 y, Dqn_f32 max_x, Dqn_f32 max_y) : min(x, y), max(max_x, max_y) {} - constexpr Dqn_b32 operator==(Dqn_Rect other) const { return (min == other.min) && (max == other.max); } -}; - -struct Dqn_RectI32 -{ - Dqn_V2I min, max; - Dqn_RectI32() = default; - Dqn_RectI32(Dqn_V2I min, Dqn_V2I max) : min(min), max(max) {} -}; - -DQN_API Dqn_Rect Dqn_Rect_InitFromPosAndSize(Dqn_V2 const &pos, Dqn_V2 const &size); -DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect const &rect); -DQN_API Dqn_b32 Dqn_Rect_ContainsPoint (Dqn_Rect const &rect, Dqn_V2 const &p); -DQN_API Dqn_b32 Dqn_Rect_ContainsRect (Dqn_Rect const &a, Dqn_Rect const &b); -DQN_API Dqn_V2 Dqn_Rect_Size (Dqn_Rect const &rect); -DQN_API Dqn_Rect Dqn_Rect_Move (Dqn_Rect const &src, Dqn_V2 const &move_amount); -DQN_API Dqn_Rect Dqn_Rect_MoveTo (Dqn_Rect const &src, Dqn_V2 const &dest); -DQN_API Dqn_b32 Dqn_Rect_Intersects (Dqn_Rect const &a, Dqn_Rect const &b); -DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect const &a, Dqn_Rect const &b); -DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect const &a, Dqn_Rect const &b); -DQN_API Dqn_Rect Dqn_Rect_FromRectI32 (Dqn_RectI32 const &a); -DQN_API Dqn_V2I Dqn_RectI32_Size (Dqn_RectI32 const &rect); - -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Math Utils -// -// ------------------------------------------------------------------------------------------------- -DQN_API Dqn_V2 Dqn_LerpV2 (Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); -DQN_API Dqn_f32 Dqn_LerpF32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); - // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_Map @@ -1135,6 +983,25 @@ enum struct Dqn_AllocatorType #define DQN_ALLOCATOR_CUSTOM_FREE_PROC(name) Dqn_isize name(void *ptr, void *user_context) typedef DQN_ALLOCATOR_CUSTOM_ALLOCATE_PROC(Dqn_Allocator_CustomAllocateProc); typedef DQN_ALLOCATOR_CUSTOM_FREE_PROC(Dqn_Allocator_CustomFreeProc); + +// Example of a custom allocator proc +/* +DQN_ALLOCATOR_CUSTOM_ALLOCATE_PROC(Dqn_Allocator_MallocProc) +{ + Dqn_usize allocation_size = Dqn_PointerMetadata_SizeRequired(size, alignment); + void * result = malloc(allocation_size); + return result; +} + +DQN_ALLOCATOR_CUSTOM_FREE_PROC(Dqn_Allocator_FreeProc) +{ + Dqn_PointerMetadata meta = Dqn_PointerMetadata_Get(ptr); + void * raw_ptr = Dqn_PointerMetadata_GetRawPointer(ptr); + free(raw_ptr, meta.size); + return meta.size; +} +*/ + struct Dqn_Allocator { Dqn_AllocatorType type; @@ -1327,6 +1194,224 @@ template DQN_API Dqn_b32 Dqn_FixedString_Append template DQN_API Dqn_b32 Dqn_FixedString_Append (Dqn_FixedString *str, Dqn_String src); template DQN_API Dqn_String Dqn_FixedString_ToString (Dqn_FixedString const *str); +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Math +// +// ------------------------------------------------------------------------------------------------- +struct Dqn_V2I +{ + Dqn_i32 x, y; + + Dqn_V2I() = default; + Dqn_V2I(Dqn_f32 x_, Dqn_f32 y_): x((Dqn_i32)x_), y((Dqn_i32)y_) {} + Dqn_V2I(Dqn_i32 x_, Dqn_i32 y_): x(x_), y(y_) {} + Dqn_V2I(Dqn_i32 xy): x(xy), y(xy) {} + + bool operator!=(Dqn_V2I other) const { return !(*this == other); } + bool operator==(Dqn_V2I other) const { return (x == other.x) && (y == other.y); } + bool operator>=(Dqn_V2I other) const { return (x >= other.x) && (y >= other.y); } + bool operator<=(Dqn_V2I other) const { return (x <= other.x) && (y <= other.y); } + bool operator< (Dqn_V2I other) const { return (x < other.x) && (y < other.y); } + bool operator> (Dqn_V2I other) const { return (x > other.x) && (y > other.y); } + Dqn_V2I operator- (Dqn_V2I other) const { Dqn_V2I result(x - other.x, y - other.y); return result; } + Dqn_V2I operator+ (Dqn_V2I other) const { Dqn_V2I result(x + other.x, y + other.y); return result; } + Dqn_V2I operator* (Dqn_V2I other) const { Dqn_V2I result(x * other.x, y * other.y); return result; } + Dqn_V2I operator* (Dqn_f32 other) const { Dqn_V2I result(x * other, y * other); return result; } + Dqn_V2I operator* (Dqn_i32 other) const { Dqn_V2I result(x * other, y * other); return result; } + Dqn_V2I operator/ (Dqn_V2I other) const { Dqn_V2I result(x / other.x, y / other.y); return result; } + Dqn_V2I operator/ (Dqn_f32 other) const { Dqn_V2I result(x / other, y / other); return result; } + Dqn_V2I operator/ (Dqn_i32 other) const { Dqn_V2I result(x / other, y / other); return result; } + Dqn_V2I &operator*=(Dqn_V2I other) { *this = *this * other; return *this; } + Dqn_V2I &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } + Dqn_V2I &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } + Dqn_V2I &operator-=(Dqn_V2I other) { *this = *this - other; return *this; } + Dqn_V2I &operator+=(Dqn_V2I other) { *this = *this + other; return *this; } +}; + +struct Dqn_V2 +{ + Dqn_f32 x, y; + + Dqn_V2() = default; + Dqn_V2(Dqn_f32 a) : x(a), y(a) {} + Dqn_V2(Dqn_i32 a) : x((Dqn_f32)a), y((Dqn_f32)a) {} + Dqn_V2(Dqn_f32 x, Dqn_f32 y): x(x), y(y) {} + Dqn_V2(Dqn_i32 x, Dqn_i32 y): x((Dqn_f32)x), y((Dqn_f32)y) {} + Dqn_V2(Dqn_V2I a) : x((Dqn_f32)a.x),y((Dqn_f32)a.y){} + + bool operator!=(Dqn_V2 other) const { return !(*this == other); } + bool operator==(Dqn_V2 other) const { return (x == other.x) && (y == other.y); } + bool operator>=(Dqn_V2 other) const { return (x >= other.x) && (y >= other.y); } + bool operator<=(Dqn_V2 other) const { return (x <= other.x) && (y <= other.y); } + bool operator< (Dqn_V2 other) const { return (x < other.x) && (y < other.y); } + bool operator> (Dqn_V2 other) const { return (x > other.x) && (y > other.y); } + Dqn_V2 operator- (Dqn_V2 other) const { Dqn_V2 result(x - other.x, y - other.y); return result; } + Dqn_V2 operator+ (Dqn_V2 other) const { Dqn_V2 result(x + other.x, y + other.y); return result; } + Dqn_V2 operator* (Dqn_V2 other) const { Dqn_V2 result(x * other.x, y * other.y); return result; } + Dqn_V2 operator* (Dqn_f32 other) const { Dqn_V2 result(x * other, y * other); return result; } + Dqn_V2 operator* (Dqn_i32 other) const { Dqn_V2 result(x * other, y * other); return result; } + Dqn_V2 operator/ (Dqn_V2 other) const { Dqn_V2 result(x / other.x, y / other.y); return result; } + Dqn_V2 operator/ (Dqn_f32 other) const { Dqn_V2 result(x / other, y / other); return result; } + Dqn_V2 operator/ (Dqn_i32 other) const { Dqn_V2 result(x / other, y / other); return result; } + Dqn_V2 &operator*=(Dqn_V2 other) { *this = *this * other; return *this; } + Dqn_V2 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } + Dqn_V2 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } + Dqn_V2 &operator/=(Dqn_V2 other) { *this = *this / other; return *this; } + Dqn_V2 &operator/=(Dqn_f32 other) { *this = *this / other; return *this; } + Dqn_V2 &operator/=(Dqn_i32 other) { *this = *this / other; return *this; } + Dqn_V2 &operator-=(Dqn_V2 other) { *this = *this - other; return *this; } + Dqn_V2 &operator+=(Dqn_V2 other) { *this = *this + other; return *this; } +}; + +struct Dqn_V3 +{ + Dqn_f32 x, y, z; + + Dqn_V3() = default; + Dqn_V3(Dqn_f32 a) : x(a), y(a), z(a) {} + Dqn_V3(Dqn_i32 a) : x(DQN_CAST(Dqn_f32)a), y(DQN_CAST(Dqn_f32)a), z(DQN_CAST(Dqn_f32)a) {} + Dqn_V3(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z): x(x), y(y), z(z) {} + Dqn_V3(Dqn_i32 x, Dqn_i32 y, Dqn_f32 z): x(DQN_CAST(Dqn_f32)x), y(DQN_CAST(Dqn_f32)y), z(DQN_CAST(Dqn_f32)z) {} + Dqn_V3(Dqn_V2 xy, Dqn_f32 z) : x(xy.x), y(xy.y), z(z) {} + + bool operator!= (Dqn_V3 other) const { return !(*this == other); } + bool operator== (Dqn_V3 other) const { return (x == other.x) && (y == other.y) && (z == other.z); } + bool operator>= (Dqn_V3 other) const { return (x >= other.x) && (y >= other.y) && (z >= other.z); } + bool operator<= (Dqn_V3 other) const { return (x <= other.x) && (y <= other.y) && (z <= other.z); } + bool operator< (Dqn_V3 other) const { return (x < other.x) && (y < other.y) && (z < other.z); } + bool operator> (Dqn_V3 other) const { return (x > other.x) && (y > other.y) && (z > other.z); } + Dqn_V3 operator- (Dqn_V3 other) const { Dqn_V3 result(x - other.x, y - other.y, z - other.z); return result; } + Dqn_V3 operator+ (Dqn_V3 other) const { Dqn_V3 result(x + other.x, y + other.y, z + other.z); return result; } + Dqn_V3 operator* (Dqn_V3 other) const { Dqn_V3 result(x * other.x, y * other.y, z * other.z); return result; } + Dqn_V3 operator* (Dqn_f32 other) const { Dqn_V3 result(x * other, y * other, z * other); return result; } + Dqn_V3 operator* (Dqn_i32 other) const { Dqn_V3 result(x * other, y * other, z * other); return result; } + Dqn_V3 operator/ (Dqn_V3 other) const { Dqn_V3 result(x / other.x, y / other.y, z / other.z); return result; } + Dqn_V3 operator/ (Dqn_f32 other) const { Dqn_V3 result(x / other, y / other, z / other); return result; } + Dqn_V3 operator/ (Dqn_i32 other) const { Dqn_V3 result(x / other, y / other, z / other); return result; } + Dqn_V3 &operator*=(Dqn_V3 other) { *this = *this * other; return *this; } + Dqn_V3 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } + Dqn_V3 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } + Dqn_V3 &operator/=(Dqn_V3 other) { *this = *this / other; return *this; } + Dqn_V3 &operator/=(Dqn_f32 other) { *this = *this / other; return *this; } + Dqn_V3 &operator/=(Dqn_i32 other) { *this = *this / other; return *this; } + Dqn_V3 &operator-=(Dqn_V3 other) { *this = *this - other; return *this; } + Dqn_V3 &operator+=(Dqn_V3 other) { *this = *this + other; return *this; } +}; + +union Dqn_V4 +{ + struct { Dqn_f32 x, y, z, w; }; + struct { Dqn_f32 r, g, b, a; }; + struct { Dqn_V2 min; Dqn_V2 max; } v2; + Dqn_V3 rgb; + Dqn_f32 e[4]; + + Dqn_V4() = default; + Dqn_V4(Dqn_f32 xyzw) : x(xyzw), y(xyzw), z(xyzw), w(xyzw) {} + Dqn_V4(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z, Dqn_f32 w): x(x), y(y), z(z), w(w) {} + Dqn_V4(Dqn_i32 x, Dqn_i32 y, Dqn_i32 z, Dqn_i32 w): x(DQN_CAST(Dqn_f32)x), y(DQN_CAST(Dqn_f32)y), z(DQN_CAST(Dqn_f32)z), w(DQN_CAST(Dqn_f32)w) {} + Dqn_V4(Dqn_V3 xyz, Dqn_f32 w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {} + Dqn_V4(Dqn_V2 v2) : x(v2.x), y(v2.y), z(v2.x), w(v2.y) {} + + bool operator!=(Dqn_V4 other) const { return !(*this == other); } + bool operator==(Dqn_V4 other) const { return (x == other.x) && (y == other.y) && (z == other.z) && (w == other.w); } + bool operator>=(Dqn_V4 other) const { return (x >= other.x) && (y >= other.y) && (z >= other.z) && (w >= other.w); } + bool operator<=(Dqn_V4 other) const { return (x <= other.x) && (y <= other.y) && (z <= other.z) && (w <= other.w); } + bool operator< (Dqn_V4 other) const { return (x < other.x) && (y < other.y) && (z < other.z) && (w < other.w); } + bool operator> (Dqn_V4 other) const { return (x > other.x) && (y > other.y) && (z > other.z) && (w > other.w); } + Dqn_V4 operator- (Dqn_V4 other) const { Dqn_V4 result(x - other.x, y - other.y, z - other.z, w - other.w); return result; } + Dqn_V4 operator+ (Dqn_V4 other) const { Dqn_V4 result(x + other.x, y + other.y, z + other.z, w + other.w); return result; } + Dqn_V4 operator* (Dqn_V4 other) const { Dqn_V4 result(x * other.x, y * other.y, z * other.z, w * other.w); return result; } + Dqn_V4 operator* (Dqn_f32 other) const { Dqn_V4 result(x * other, y * other, z * other, w * other); return result; } + Dqn_V4 operator* (Dqn_i32 other) const { Dqn_V4 result(x * other, y * other, z * other, w * other); return result; } + Dqn_V4 operator/ (Dqn_f32 other) const { Dqn_V4 result(x / other, y / other, z / other, w / other); return result; } + Dqn_V4 &operator*=(Dqn_V4 other) { *this = *this * other; return *this; } + Dqn_V4 &operator*=(Dqn_f32 other) { *this = *this * other; return *this; } + Dqn_V4 &operator*=(Dqn_i32 other) { *this = *this * other; return *this; } + Dqn_V4 &operator-=(Dqn_V4 other) { *this = *this - other; return *this; } + Dqn_V4 &operator+=(Dqn_V4 other) { *this = *this + other; return *this; } +}; + +// NOTE: Column major matrix +struct Dqn_M4 +{ + Dqn_f32 columns[4][4]; +}; + +DQN_API Dqn_V2I Dqn_V2_ToV2I (Dqn_V2 a); +DQN_API Dqn_V2 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a); +DQN_API Dqn_f32 Dqn_V2_Dot (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_f32 Dqn_V2_LengthSq (Dqn_V2 a, Dqn_V2 b); +DQN_API Dqn_V2 Dqn_V2_Normalise (Dqn_V2 a); +DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a); + +DQN_API Dqn_f32 Dqn_V3_LengthSq (Dqn_V3 a); +DQN_API Dqn_f32 Dqn_V3_Length (Dqn_V3 a); +DQN_API Dqn_V3 Dqn_V3_Normalise (Dqn_V3 a); + +DQN_API Dqn_f32 Dqn_V4_Dot (Dqn_V4 a, Dqn_V4 b); + +DQN_API Dqn_M4 Dqn_M4_Identity (); +DQN_API Dqn_M4 Dqn_M4_ScaleF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z); +DQN_API Dqn_M4 Dqn_M4_Scale (Dqn_V3 xyz); +DQN_API Dqn_M4 Dqn_M4_TranslateF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z); +DQN_API Dqn_M4 Dqn_M4_Translate (Dqn_V3 xyz); +DQN_API Dqn_M4 Dqn_M4_Transpose (Dqn_M4 mat); +DQN_API Dqn_M4 Dqn_M4_Rotate (Dqn_V3 axis, Dqn_f32 radians); +DQN_API Dqn_M4 Dqn_M4_Orthographic (Dqn_f32 left, Dqn_f32 right, Dqn_f32 bottom, Dqn_f32 top, Dqn_f32 z_near, Dqn_f32 z_far); +DQN_API Dqn_M4 Dqn_M4_Perspective (Dqn_f32 fov /*radians*/, Dqn_f32 aspect, Dqn_f32 z_near, Dqn_f32 z_far); +DQN_API Dqn_M4 Dqn_M4_Add (Dqn_M4 lhs, Dqn_M4 rhs); +DQN_API Dqn_M4 Dqn_M4_Sub (Dqn_M4 lhs, Dqn_M4 rhs); +DQN_API Dqn_M4 Dqn_M4_Mul (Dqn_M4 lhs, Dqn_M4 rhs); +DQN_API Dqn_M4 Dqn_M4_Div (Dqn_M4 lhs, Dqn_M4 rhs); +DQN_API Dqn_M4 Dqn_M4_AddF (Dqn_M4 lhs, Dqn_f32 rhs); +DQN_API Dqn_M4 Dqn_M4_SubF (Dqn_M4 lhs, Dqn_f32 rhs); +DQN_API Dqn_M4 Dqn_M4_MulF (Dqn_M4 lhs, Dqn_f32 rhs); +DQN_API Dqn_M4 Dqn_M4_DivF (Dqn_M4 lhs, Dqn_f32 rhs); + +DQN_API Dqn_FixedString<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat); + +struct Dqn_Rect +{ + Dqn_V2 min, max; + Dqn_Rect() = default; + Dqn_Rect(Dqn_V2 min, Dqn_V2 max) : min(min), max(max) {} + Dqn_Rect(Dqn_V2I min, Dqn_V2I max) : min(min), max(max) {} + Dqn_Rect(Dqn_f32 x, Dqn_f32 y, Dqn_f32 max_x, Dqn_f32 max_y) : min(x, y), max(max_x, max_y) {} + Dqn_b32 operator==(Dqn_Rect other) const { return (min == other.min) && (max == other.max); } +}; + +struct Dqn_RectI32 +{ + Dqn_V2I min, max; + Dqn_RectI32() = default; + Dqn_RectI32(Dqn_V2I min, Dqn_V2I max) : min(min), max(max) {} +}; + +DQN_API Dqn_Rect Dqn_Rect_InitFromPosAndSize(Dqn_V2 pos, Dqn_V2 size); +DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect); +DQN_API Dqn_b32 Dqn_Rect_ContainsPoint (Dqn_Rect rect, Dqn_V2 p); +DQN_API Dqn_b32 Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_V2 Dqn_Rect_Size (Dqn_Rect rect); +DQN_API Dqn_Rect Dqn_Rect_Move (Dqn_Rect src, Dqn_V2 move_amount); +DQN_API Dqn_Rect Dqn_Rect_MoveTo (Dqn_Rect src, Dqn_V2 dest); +DQN_API Dqn_b32 Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b); +DQN_API Dqn_Rect Dqn_Rect_FromRectI32 (Dqn_RectI32 a); +DQN_API Dqn_V2I Dqn_RectI32_Size (Dqn_RectI32 rect); + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Math Utils +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_V2 Dqn_LerpV2 (Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); +DQN_API Dqn_f32 Dqn_LerpF32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); + // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_StringMap @@ -1570,24 +1655,100 @@ enum struct Dqn_EpochTimeFormat // timestamp: Unix epoch timestamp DQN_API char *Dqn_EpochTimeToLocalDate(Dqn_i64 timestamp, char *buf, Dqn_isize buf_len); -struct Dqn_Timer +DQN_API Dqn_u64 Dqn_PerfCounter_Now (); +DQN_API Dqn_f64 Dqn_PerfCounter_S (Dqn_u64 begin, Dqn_u64 end); +DQN_API Dqn_f64 Dqn_PerfCounter_Ms (Dqn_u64 begin, Dqn_u64 end); +DQN_API Dqn_f64 Dqn_PerfCounter_MicroS(Dqn_u64 begin, Dqn_u64 end); +DQN_API Dqn_f64 Dqn_PerfCounter_Ns (Dqn_u64 begin, Dqn_u64 end); + +struct Dqn_Timer // NOTE: Uses the PerfCounter API { -#if defined(DQN_OS_WIN32) - LARGE_INTEGER start; - LARGE_INTEGER end; -#endif + Dqn_u64 start; + Dqn_u64 end; }; DQN_API Dqn_Timer Dqn_Timer_Begin (); DQN_API void Dqn_Timer_End (Dqn_Timer *timer); -DQN_API Dqn_u64 Dqn_Timer_S (Dqn_Timer const *timer); -DQN_API Dqn_u64 Dqn_Timer_Ms (Dqn_Timer const *timer); -DQN_API Dqn_u64 Dqn_Timer_MicroS(Dqn_Timer const *timer); -DQN_API Dqn_u64 Dqn_Timer_Ns (Dqn_Timer const *timer); +DQN_API Dqn_f64 Dqn_Timer_S (Dqn_Timer timer); +DQN_API Dqn_f64 Dqn_Timer_Ms (Dqn_Timer timer); +DQN_API Dqn_f64 Dqn_Timer_MicroS(Dqn_Timer timer); +DQN_API Dqn_f64 Dqn_Timer_Ns (Dqn_Timer timer); DQN_API char *Dqn_U64ToStr (Dqn_u64 val, Dqn_U64Str *result, Dqn_b32 comma_sep); DQN_API char *Dqn_U64ToTempStr (Dqn_u64 val, Dqn_b32 comma_sep = true); +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_Win32 +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_FixedString<1024> Dqn_Win32_LastError(); +DQN_API wchar_t *Dqn_Win32_ArenaToWChar(Dqn_ArenaAllocator *arena, Dqn_String src, int *wchar_size); + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_TimedBlock +// +// ------------------------------------------------------------------------------------------------ +// TimedBlock provides a extremely primitive way of measuring the duration of +// code blocks, by sprinkling DQN_TIMED_BLOCK_RECORD("record label"), you can +// measure the time between the macro and the next record call. +// +// Example: Record the duration of the for-loop below and print it at the end. +/* + int main() + { + DQN_TIMED_BLOCK_INIT("Profiling Region", 32); // name, records to allocate + DQN_TIMED_BLOCK_RECORD("a"); + for (int unused1_ = 0; unused1_ < 1000000; unused1_++) + { + for (int unused2_ = 0; unused2_ < 1000000; unused2_++) + { + (void)unused1_; + (void)unused2_; + } + } + DQN_TIMED_BLOCK_RECORD("b"); + DQN_TIMED_BLOCK_DUMP; + return 0; + } +*/ +struct Dqn_TimedBlock +{ + char const *label; + Dqn_u64 tick; +}; + +// Initialise a timing block region, see DQN_TIMED_BLOCK_INIT_SCOPED but does +// *not* automatically dump the timings, it must manually be caleld via +// DQN_TIMED_BLOCK_DUMP +#define DQN_TIMED_BLOCK_INIT(label, size) \ + Dqn_TimedBlock timings_[size]; \ + Dqn_usize timings_size_ = 0; \ + DQN_TIMED_BLOCK_RECORD(label) + +// Add a timing record to the previous timing block +// label: The label to give +#define DQN_TIMED_BLOCK_RECORD(label) timings_[timings_size_++] = {label, Dqn_PerfCounter_Now()} + +// Dump the timing block via Dqn_Log +#define DQN_TIMED_BLOCK_DUMP \ + DQN_ASSERT_MSG(timings_size_ < sizeof(timings_) / sizeof(timings_[0]), \ + "Timings array indexed out-of-bounds, use a bigger size"); \ + for (int timings_index_ = 0; timings_index_ < (timings_size_ - 1); timings_index_++) \ + { \ + Dqn_TimedBlock t1 = timings_[timings_index_ + 0]; \ + Dqn_TimedBlock t2 = timings_[timings_index_ + 1]; \ + DQN_LOG_P("%s -> %s: %fms", t1.label, t2.label, Dqn_PerfCounter_Ms(t1.tick, t2.tick)); \ + } \ + \ + if (timings_size_ >= 1) \ + { \ + Dqn_TimedBlock t1 = timings_[0]; \ + Dqn_TimedBlock t2 = timings_[timings_size_ - 1]; \ + DQN_LOG_P("%s -> %s (total): %fms", t1.label, t2.label, Dqn_PerfCounter_Ms(t1.tick, t2.tick)); \ + } + // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_StringBuilder @@ -1766,6 +1927,15 @@ struct Dqn_List template DQN_API Dqn_List Dqn_List_InitWithArena (Dqn_ArenaAllocator *arena, Dqn_isize chunk_size = 128); template DQN_API Dqn_List Dqn_List_InitWithAllocator(Dqn_Allocator *allocator, Dqn_isize chunk_size = 128); + +// Produce an iterator for the data in the list +/* + Dqn_List list = {}; + for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&list, &it);) + { + int *item = it.data; + } +*/ template DQN_API Dqn_b32 Dqn_List_Iterate (Dqn_List *list, Dqn_ListIterator *iterator); #define Dqn_List_TaggedMake( list, count, tag) Dqn_List__Make(list, count DQN_CALL_SITE(tag)) @@ -1950,7 +2120,13 @@ DQN_API Dqn_b32 Dqn_FixedString_AppendFmtV(Dqn_FixedString *str, char cons Dqn_isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; Dqn_isize space = MAX_ - str->size; Dqn_b32 result = require <= space; - DQN_ASSERT_MSG(require <= space, "(require=%I64d, space=%I64d)", require, space); + + if (!result) + { + DQN_LOG_W("Insufficient space in string: require=%I64d, space=%I64d", require, space); + return result; + } + str->size += stbsp_vsnprintf(str->data + str->size, static_cast(space), fmt, va2); va_end(va2); return result; @@ -1971,10 +2147,13 @@ DQN_API Dqn_b32 Dqn_FixedString_Append(Dqn_FixedString *str, char const *s { if (size == -1) size = DQN_CAST(Dqn_isize)Dqn_Str_Len(src); Dqn_isize space = MAX_ - str->size; + Dqn_b32 result = size <= space; - Dqn_b32 result = true; - DQN_ASSERT_MSG_IF(size >= space, "size: %jd, space: %jd", size, space) - return false; + if (!result) + { + DQN_LOG_W("Insufficient space in string: size=%I64d, space=%I64d", size, space); + return result; + } DQN_MEMCOPY(str->data + str->size, src, size); str->size += size; @@ -2259,7 +2438,7 @@ DQN_API char *Dqn_StringBuilder__AllocateWriteBuffer(Dqn_StringBuilder *build if (new_block_needed) { Dqn_Allocator *allocator = builder->allocator ? builder->allocator : &builder->backup_allocator; - Dqn_isize allocation_size = DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_BLOCK_SIZE); + Dqn_isize allocation_size = DQN_M_MAX(size_required, DQN_STRING_BUILDER_MIN_BLOCK_SIZE); block = Dqn_Allocator_New(allocator, Dqn_StringBuilderBlock, Dqn_ZeroMem::No); if (!block) return nullptr; @@ -2333,7 +2512,7 @@ DQN_API void Dqn_StringBuilder_BuildToDest(Dqn_StringBuilder const *builder, block = block->next, remaining_space = end - ptr) { Dqn_isize num_bytes = block->used; - Dqn_isize bytes_to_copy = DQN_MIN(num_bytes, remaining_space); + Dqn_isize bytes_to_copy = DQN_M_MIN(num_bytes, remaining_space); DQN_MEMCOPY(ptr, block->mem, bytes_to_copy); ptr += bytes_to_copy; } @@ -2432,7 +2611,7 @@ template void Dqn__EraseStableFromCArray(T *array, Dqn_isize size, { DQN_ASSERT(index >= 0 && index < size); DQN_ASSERT(size <= max); (void)max; - Dqn_isize next_index = DQN_MIN(index + 1, size); + Dqn_isize next_index = DQN_M_MIN(index + 1, size); Dqn_usize bytes_to_copy = (size - next_index) * sizeof(T); memmove(array + index, array + next_index, bytes_to_copy); } @@ -2635,7 +2814,7 @@ DQN_API bool Dqn_Array__GrowIfNeeded(Dqn_Array *a, Dqn_isize num_to_add DQN_C bool result = true; if (new_size > a->max) { - Dqn_isize num_items = DQN_MAX(4, DQN_MAX(new_size, (a->max * 2))); + Dqn_isize num_items = DQN_M_MAX(4, DQN_M_MAX(new_size, (a->max * 2))); result = Dqn_Array__Reserve(a, num_items DQN_CALL_SITE_ARGS_INPUT); } @@ -2746,7 +2925,7 @@ DQN_API T *Dqn_List__Make(Dqn_List *list, Dqn_isize count DQN_CALL_SITE_ARGS) if (!tail) return nullptr; - Dqn_isize items = DQN_MAX(list->chunk_size, count); + Dqn_isize items = DQN_M_MAX(list->chunk_size, count); tail->data = (T * )Dqn_Allocator__Allocate(allocator, sizeof(T) * items, alignof(T), Dqn_ZeroMem::Yes DQN_CALL_SITE_ARGS_INPUT); tail->size = items; @@ -2804,7 +2983,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) return result; } -#if defined(DQN_COMPILER_MSVC) +#if defined(DQN_COMPILER_W32_MSVC) #pragma warning(pop) #endif #endif // DQN_H @@ -2900,6 +3079,11 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) // #define INFINITE 0xFFFFFFFF // Infinite timeout + // + // NOTE: MultiByteToWideChar + // + #define CP_UTF8 65001 // UTF-8 translation + // // NOTE: FormatMessageA // @@ -2926,6 +3110,8 @@ 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); @@ -2943,6 +3129,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) void *GetProcAddress (void *hmodule, char const *proc_name); void *LoadLibraryA (char const *file_name); void *VirtualAlloc (void *address, size_t size, DWORD allocation_type, DWORD protect); + int MultiByteToWideChar (unsigned int CodePage, DWORD dwFlags, char const *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); } #endif // !defined(DQN_NO_WIN32_MINIMAL_HEADER) #else // !defined(DQN_OS_WIN32) @@ -2982,10 +3169,10 @@ DQN_API void Dqn__ZeroMemBytes(void *ptr, Dqn_usize count, Dqn_ZeroMem zero_mem) Dqn_CPUIDRegisters Dqn_CPUID(int function_id) { Dqn_CPUIDRegisters result = {}; -#if defined(DQN_COMPILER_MSVC) - __cpuid((int *)result.array, function_id); +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) + __cpuid(DQN_CAST(int *)result.array, function_id); #elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) - __get_cpuid(function_id, &result.eax, &result.ebx, &result.ecx, &result.edx); + __get_cpuid(function_id, &result.array[0] /*eax*/, &result.array[1] /*ebx*/, &result.array[2] /*ecx*/ , &result.array[3] /*edx*/); #else #error "Compiler not supported" #endif @@ -3000,7 +3187,7 @@ Dqn_CPUIDRegisters Dqn_CPUID(int function_id) void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex) { unsigned int ticket = Dqn_AtomicAddU32(&mutex->ticket, 1); - while(ticket != mutex->serving); + Dqn_TicketMutex_BeginTicket(mutex, ticket); } void Dqn_TicketMutex_End(Dqn_TicketMutex *mutex) @@ -3008,6 +3195,37 @@ void Dqn_TicketMutex_End(Dqn_TicketMutex *mutex) Dqn_AtomicAddU32(&mutex->serving, 1); } +unsigned int Dqn_TicketMutex_MakeTicket(Dqn_TicketMutex *mutex) +{ + unsigned int result = Dqn_AtomicAddU32(&mutex->ticket, 1); + return result; +} + +void Dqn_TicketMutex_BeginTicket(const Dqn_TicketMutex *mutex, unsigned int ticket) +{ + DQN_ASSERT_MSG(ticket <= mutex->serving, + "Mutex skipped ticket? Was ticket generated by the correct mutex via MakeTicket? ticket = %u, " + "mutex->serving = %u", + ticket, + mutex->serving); + while(ticket != mutex->serving) + { + // NOTE: Use spinlock intrinsic + _mm_pause(); + } +} + +Dqn_b32 Dqn_TicketMutex_CanLock(const Dqn_TicketMutex *mutex, unsigned int ticket) +{ + DQN_ASSERT_MSG(ticket <= mutex->serving, + "Mutex skipped ticket? Was ticket generated by the correct mutex via MakeTicket? ticket = %u, " + "mutex->serving = %u", + ticket, + mutex->serving); + Dqn_b32 result = (ticket == mutex->serving); + return result; +} + // ------------------------------------------------------------------------------------------------- // @@ -3072,7 +3290,7 @@ DQN_API void Dqn_LogV(Dqn_LogType type, FILE *handle = (type == Dqn_LogType::Error) ? stderr : stdout; fprintf(handle, - "[%s:%.*s:%05I64u:%.*s] ", + "[%s] %.*s:%05I64u:%.*s ", Dqn_LogTypeString[DQN_CAST(int) type], file_name_len, file_name, @@ -3206,195 +3424,6 @@ DQN_API char *Dqn_PointerMetadata_GetRawPointer(void *ptr) return result; } -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Dqn_V2 -// -// ------------------------------------------------------------------------------------------------- -DQN_API Dqn_V2I Dqn_V2_ToV2I(Dqn_V2 a) -{ - Dqn_V2I result(static_cast(a.x), static_cast(a.y)); - return result; -} - -DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b) -{ - Dqn_V2 result = Dqn_V2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y)); - return result; -} - -DQN_API Dqn_V2 Dqn_V2_Max(Dqn_V2 a, Dqn_V2 b) -{ - Dqn_V2 result = Dqn_V2(DQN_MAX(a.x, b.x), DQN_MAX(a.y, b.y)); - return result; -} - -DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a) -{ - Dqn_V2 result = Dqn_V2(DQN_ABS(a.x), DQN_ABS(a.y)); - return result; -} - -DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b) -{ - Dqn_f32 result = (a.x * b.x) + (a.y * b.y); - return result; -} - -DQN_API Dqn_f32 Dqn_V2_LengthSq(Dqn_V2 a, Dqn_V2 b) -{ - Dqn_f32 x_side = b.x - a.x; - Dqn_f32 y_side = b.y - a.y; - Dqn_f32 result = DQN_SQUARED(x_side) + DQN_SQUARED(y_side); - return result; -} - -DQN_API Dqn_V2 Dqn_V2_Normalise(Dqn_V2 a) -{ - Dqn_f32 length_sq = DQN_SQUARED(a.x) + DQN_SQUARED(a.y); - Dqn_f32 length = DQN_SQRTF(length_sq); - Dqn_V2 result = a / length; - return result; -} - -DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a) -{ - Dqn_V2 result = Dqn_V2(-a.y, a.x); - return result; -} - -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Dqn_V4 -// -// ------------------------------------------------------------------------------------------------- -DQN_API Dqn_f32 Dqn_V4_Dot(Dqn_V4 const *a, Dqn_V4 const *b) -{ - Dqn_f32 result = (a->x * b->x) + (a->y * b->y) + (a->z * b->z) + (a->w * b->w); - return result; -} - -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Dqn_Rect -// -// ------------------------------------------------------------------------------------------------- -DQN_API Dqn_Rect Dqn_Rect_InitFromPosAndSize(Dqn_V2 const &pos, Dqn_V2 const &size) -{ - Dqn_Rect result = {}; - result.min = pos; - if (size.w < 0) result.min.x -= size.w; - if (size.h < 0) result.min.y -= size.h; - result.max = result.min + Dqn_V2_Abs(size); - return result; -} - -DQN_API Dqn_V2 Dqn_Rect_Center(Dqn_Rect const &rect) -{ - Dqn_V2 size = rect.max - rect.min; - Dqn_V2 result = rect.min + (size * 0.5f); - return result; -} - -DQN_API Dqn_b32 Dqn_Rect_ContainsPoint(Dqn_Rect const &rect, Dqn_V2 const &p) -{ - Dqn_b32 result = (p.x >= rect.min.x && p.x <= rect.max.x && p.y >= rect.min.y && p.y <= rect.max.y); - return result; -} - - -DQN_API Dqn_b32 Dqn_Rect_ContainsRect(Dqn_Rect const &a, Dqn_Rect const &b) -{ - Dqn_b32 result = (b.min >= a.min && b.max <= a.max); - return result; -} - - -DQN_API Dqn_V2 Dqn_Rect_Size(Dqn_Rect const &rect) -{ - Dqn_V2 result = rect.max - rect.min; - return result; -} - -DQN_API Dqn_Rect Dqn_Rect_Move(Dqn_Rect const &src, Dqn_V2 const &move_amount) -{ - Dqn_Rect result = src; - result.min += move_amount; - result.max += move_amount; - return result; -} - -DQN_API Dqn_Rect Dqn_Rect_MoveTo(Dqn_Rect const &src, Dqn_V2 const &dest) -{ - Dqn_V2 move_amount = dest - src.min; - Dqn_Rect result = src; - result.min += move_amount; - result.max += move_amount; - return result; -} - -DQN_API Dqn_b32 Dqn_Rect_Intersects(Dqn_Rect const &a, Dqn_Rect const &b) -{ - Dqn_b32 result = (a.min.x <= b.max.x && a.max.x >= b.min.x) && - (a.min.y <= b.max.y && a.max.y >= b.min.y); - return result; -} - -DQN_API Dqn_Rect Dqn_Rect_Intersection(Dqn_Rect const &a, Dqn_Rect const &b) -{ - Dqn_Rect result = {}; - if (Dqn_Rect_Intersects(a, b)) - { - result.min.x = DQN_MAX(a.min.x, b.min.x); - result.min.y = DQN_MAX(a.min.y, b.min.y); - result.max.x = DQN_MIN(a.max.x, b.max.x); - result.max.y = DQN_MIN(a.max.y, b.max.y); - } - - return result; -} - -DQN_API Dqn_Rect Dqn_Rect_Union(Dqn_Rect const &a, Dqn_Rect const &b) -{ - Dqn_Rect result = {}; - result.min.x = DQN_MIN(a.min.x, b.min.x); - result.min.y = DQN_MIN(a.min.y, b.min.y); - result.max.x = DQN_MAX(a.max.x, b.max.x); - result.max.y = DQN_MAX(a.max.y, b.max.y); - return result; -} - -DQN_API Dqn_Rect Dqn_Rect_FromRectI32(Dqn_RectI32 const &a) -{ - Dqn_Rect result = Dqn_Rect(a.min, a.max); - return result; -} - -DQN_API Dqn_V2I Dqn_RectI32_Size(Dqn_RectI32 const &rect) -{ - Dqn_V2I result = rect.max - rect.min; - return result; -} - -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Math Utils -// -// ------------------------------------------------------------------------------------------------- -DQN_API Dqn_V2 Dqn_LerpV2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b) -{ - Dqn_V2 result = {}; - result.x = a.x + ((b.x - a.x) * t); - result.y = a.y + ((b.y - a.y) * t); - return result; -} - -DQN_API Dqn_f32 Dqn_LerpF32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b) -{ - Dqn_f32 result = a + ((b - a) * t); - return result; -} - // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_AllocationTracer @@ -3594,7 +3623,7 @@ DQN_API void *Dqn_Allocator__Allocate(Dqn_Allocator *allocator, Dqn_isize size, default: case Dqn_AllocatorType::Null: { - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_LOG_W("Requested allocation on null allocator"); return result; } @@ -3761,7 +3790,13 @@ DQN_API Dqn_b32 Dqn_String_AppendFmtV(Dqn_String *str, char const *fmt, va_list Dqn_isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; Dqn_isize space = str->cap - str->size; Dqn_b32 result = require <= space; - DQN_ASSERT_MSG(require <= space, "(require=%I64d, space=%I64d)", require, space); + + if (!result) + { + DQN_LOG_W("Insufficient space in string: require=%I64d, space=%I64d", require, space); + return result; + } + str->size += stbsp_vsnprintf(str->str + str->size, static_cast(space), fmt, va2); va_end(va2); return result; @@ -3852,7 +3887,7 @@ DQN_API Dqn_ArenaAllocatorBlock *Dqn_ArenaAllocator__AllocateBlock(Dqn_ArenaAllo Dqn_isize min_block_size = arena->min_block_size; if (min_block_size == 0) min_block_size = DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE; - Dqn_isize mem_block_size = DQN_MAX(min_block_size, requested_size); + Dqn_isize mem_block_size = DQN_M_MAX(min_block_size, requested_size); auto const allocate_size = DQN_CAST(Dqn_isize)(sizeof(*arena->curr_mem_block) + mem_block_size); Dqn_Allocator *allocator = arena->allocator ? arena->allocator : &arena->backup_allocator; auto *result = DQN_CAST(Dqn_ArenaAllocatorBlock *)Dqn_Allocator__Allocate(allocator, allocate_size, alignof(Dqn_ArenaAllocatorBlock), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT); @@ -4102,6 +4137,464 @@ DQN_API Dqn_FixedString<512> Dqn_ArenaAllocator_StatsString(Dqn_ArenaAllocator c return result; } +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_V2 Implementation +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_V2I Dqn_V2_ToV2I(Dqn_V2 a) +{ + Dqn_V2I result = Dqn_V2I(DQN_CAST(Dqn_i32)a.x, DQN_CAST(Dqn_i32)a.y); + return result; +} + +DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b) +{ + Dqn_V2 result = Dqn_V2(DQN_M_MIN(a.x, b.x), DQN_M_MIN(a.y, b.y)); + return result; +} + +DQN_API Dqn_V2 Dqn_V2_Max(Dqn_V2 a, Dqn_V2 b) +{ + Dqn_V2 result = Dqn_V2(DQN_M_MAX(a.x, b.x), DQN_M_MAX(a.y, b.y)); + return result; +} + +DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a) +{ + Dqn_V2 result = Dqn_V2(DQN_M_ABS(a.x), DQN_M_ABS(a.y)); + return result; +} + +DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b) +{ + Dqn_f32 result = (a.x * b.x) + (a.y * b.y); + return result; +} + +DQN_API Dqn_f32 Dqn_V2_LengthSq(Dqn_V2 a, Dqn_V2 b) +{ + Dqn_f32 x_side = b.x - a.x; + Dqn_f32 y_side = b.y - a.y; + Dqn_f32 result = DQN_M_SQUARED(x_side) + DQN_M_SQUARED(y_side); + return result; +} + +DQN_API Dqn_V2 Dqn_V2_Normalise(Dqn_V2 a) +{ + Dqn_f32 length_sq = DQN_M_SQUARED(a.x) + DQN_M_SQUARED(a.y); + Dqn_f32 length = DQN_SQRTF(length_sq); + Dqn_V2 result = a / length; + return result; +} + +DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a) +{ + Dqn_V2 result = Dqn_V2(-a.y, a.x); + return result; +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_V3 Implementation +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_f32 Dqn_V3_LengthSq(Dqn_V3 a) +{ + Dqn_f32 result = DQN_M_SQUARED(a.x) + DQN_M_SQUARED(a.y) + DQN_M_SQUARED(a.z); + return result; +} + +DQN_API Dqn_f32 Dqn_V3_Length(Dqn_V3 a) +{ + Dqn_f32 length_sq = DQN_M_SQUARED(a.x) + DQN_M_SQUARED(a.y) + DQN_M_SQUARED(a.z); + Dqn_f32 result = DQN_SQRTF(length_sq); + return result; +} + +DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a) +{ + Dqn_f32 length = Dqn_V3_Length(a); + Dqn_V3 result = a / length; + return result; +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_V4 Implementation +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_f32 Dqn_V4_Dot(Dqn_V4 a, Dqn_V4 b) +{ + Dqn_f32 result = (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w); + return result; +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_M4 Implementation +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_M4 Dqn_M4_Identity() +{ + Dqn_M4 result = + {{ + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_ScaleF(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z) +{ + Dqn_M4 result = + {{ + {x, 0, 0, 0}, + {0, y, 0, 0}, + {0, 0, z, 0}, + {0, 0, 0, 1}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Scale(Dqn_V3 xyz) +{ + Dqn_M4 result = + {{ + {xyz.x, 0, 0, 0}, + {0, xyz.y, 0, 0}, + {0, 0, xyz.z, 0}, + {0, 0, 0, 1}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_TranslateF(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z) +{ + Dqn_M4 result = + {{ + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {x, y, z, 1}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Translate(Dqn_V3 xyz) +{ + Dqn_M4 result = + {{ + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {xyz.x, xyz.y, xyz.z, 1}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Transpose(Dqn_M4 mat) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + for (int row = 0; row < 4; row++) + result.columns[col][row] = mat.columns[row][col]; + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Rotate(Dqn_V3 axis01, Dqn_f32 radians) +{ + DQN_ASSERT_MSG(DQN_M_ABS(Dqn_V3_Length(axis01) - 1.f) <= 0.01f, + "Rotation axis must be normalised, length = %f", + Dqn_V3_Length(axis01)); + + Dqn_f32 sin = DQN_M_SINF(radians); + Dqn_f32 cos = DQN_M_COSF(radians); + Dqn_f32 one_minus_cos = 1.f - cos; + + Dqn_f32 x = axis01.x; + Dqn_f32 y = axis01.y; + Dqn_f32 z = axis01.z; + Dqn_f32 x2 = DQN_M_SQUARED(x); + Dqn_f32 y2 = DQN_M_SQUARED(y); + Dqn_f32 z2 = DQN_M_SQUARED(z); + + Dqn_M4 result = + {{ + {cos + x2*one_minus_cos, y*x*one_minus_cos + z*sin, z*x*one_minus_cos - y*sin, 0}, // Col 1 + {x*y*one_minus_cos - z*sin, cos + y2*one_minus_cos, z*y*one_minus_cos + x*sin, 0}, // Col 2 + {x*z*one_minus_cos + y*sin, y*z*one_minus_cos - x*sin, cos + z2*one_minus_cos, 0}, // Col 3 + {0, 0, 0, 1}, // Col 4 + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Orthographic(Dqn_f32 left, Dqn_f32 right, Dqn_f32 bottom, Dqn_f32 top, Dqn_f32 z_near, Dqn_f32 z_far) +{ + Dqn_M4 result = + {{ + {2.f / (right - left), 0.f, 0.f, 0.f}, + {0.f, 2.f / (top - bottom), 0.f, 0.f}, + {0.f, 0.f, -2.f / (z_far - z_near), 0.f}, + {(-1.f * (right + left)) / (right - left), (-1.f * (top + bottom)) / (top - bottom), (-1.f * (z_far + z_near)) / (z_far - z_near), 1.f}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Perspective(Dqn_f32 fov /*radians*/, Dqn_f32 aspect, Dqn_f32 z_near, Dqn_f32 z_far) +{ + Dqn_f32 tan_fov = DQN_M_TANF(fov / 2.f); + Dqn_M4 result = + {{ + {1.f / (aspect * tan_fov), 0.f, 0.f, 0.f}, + {0, 1.f / tan_fov, 0.f, 0.f}, + {0.f, 0.f, (z_near + z_far) / (z_near - z_far), -1.f}, + {0.f, 0.f, (2.f * z_near * z_far)/(z_near - z_far), 0.f}, + }}; + + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Add(Dqn_M4 lhs, Dqn_M4 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] + rhs.columns[col][it]; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Sub(Dqn_M4 lhs, Dqn_M4 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] - rhs.columns[col][it]; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Mul(Dqn_M4 lhs, Dqn_M4 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int row = 0; row < 4; row++) + { + Dqn_f32 sum = 0; + for (int f32_it = 0; f32_it < 4; f32_it++) + sum += lhs.columns[f32_it][row] * rhs.columns[col][f32_it]; + + result.columns[col][row] = sum; + } + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_Div(Dqn_M4 lhs, Dqn_M4 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] / rhs.columns[col][it]; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_AddF(Dqn_M4 lhs, Dqn_f32 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] + rhs; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_SubF(Dqn_M4 lhs, Dqn_f32 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] - rhs; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_MulF(Dqn_M4 lhs, Dqn_f32 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] * rhs; + } + return result; +} + +DQN_API Dqn_M4 Dqn_M4_DivF(Dqn_M4 lhs, Dqn_f32 rhs) +{ + Dqn_M4 result; + for (int col = 0; col < 4; col++) + { + for (int it = 0; it < 4; it++) + result.columns[col][it] = lhs.columns[col][it] / rhs; + } + return result; +} + +DQN_API Dqn_FixedString<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat) +{ + Dqn_FixedString<256> result = {}; + for (int row = 0; row < 4; row++) + { + for (int it = 0; it < 4; it++) + { + if (it == 0) Dqn_FixedString_Append(&result, "|"); + Dqn_FixedString_AppendFmt(&result, "%.5f", mat.columns[it][row]); + if (it != 3) Dqn_FixedString_Append(&result, ", "); + else Dqn_FixedString_Append(&result, "|\n"); + } + } + + return result; +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_Rect +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_Rect Dqn_Rect_InitFromPosAndSize(Dqn_V2 pos, Dqn_V2 size) +{ + Dqn_Rect result = {}; + result.min = pos; + if (size.x < 0) result.min.x -= size.x; + if (size.y < 0) result.min.y -= size.y; + result.max = result.min + Dqn_V2_Abs(size); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_Center(Dqn_Rect rect) +{ + Dqn_V2 size = rect.max - rect.min; + Dqn_V2 result = rect.min + (size * 0.5f); + return result; +} + +DQN_API Dqn_b32 Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p) +{ + Dqn_b32 result = (p.x >= rect.min.x && p.x <= rect.max.x && p.y >= rect.min.y && p.y <= rect.max.y); + return result; +} + +DQN_API Dqn_b32 Dqn_Rect_ContainsRect(Dqn_Rect a, Dqn_Rect b) +{ + Dqn_b32 result = (b.min >= a.min && b.max <= a.max); + return result; +} + +DQN_API Dqn_V2 Dqn_Rect_Size(Dqn_Rect rect) +{ + Dqn_V2 result = rect.max - rect.min; + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_Move(Dqn_Rect src, Dqn_V2 move_amount) +{ + Dqn_Rect result = src; + result.min += move_amount; + result.max += move_amount; + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_MoveTo(Dqn_Rect src, Dqn_V2 dest) +{ + Dqn_V2 move_amount = dest - src.min; + Dqn_Rect result = src; + result.min += move_amount; + result.max += move_amount; + return result; +} + +DQN_API Dqn_b32 Dqn_Rect_Intersects(Dqn_Rect a, Dqn_Rect b) +{ + Dqn_b32 result = (a.min.x <= b.max.x && a.max.x >= b.min.x) && + (a.min.y <= b.max.y && a.max.y >= b.min.y); + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_Intersection(Dqn_Rect a, Dqn_Rect b) +{ + Dqn_Rect result = {}; + if (Dqn_Rect_Intersects(a, b)) + { + result.min.x = DQN_M_MAX(a.min.x, b.min.x); + result.min.y = DQN_M_MAX(a.min.y, b.min.y); + result.max.x = DQN_M_MIN(a.max.x, b.max.x); + result.max.y = DQN_M_MIN(a.max.y, b.max.y); + } + + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_Union(Dqn_Rect a, Dqn_Rect b) +{ + Dqn_Rect result = {}; + result.min.x = DQN_M_MIN(a.min.x, b.min.x); + result.min.y = DQN_M_MIN(a.min.y, b.min.y); + result.max.x = DQN_M_MAX(a.max.x, b.max.x); + result.max.y = DQN_M_MAX(a.max.y, b.max.y); + return result; +} + +DQN_API Dqn_Rect Dqn_Rect_FromRectI32(Dqn_RectI32 a) +{ + Dqn_Rect result = Dqn_Rect(a.min, a.max); + return result; +} + +DQN_API Dqn_V2I Dqn_RectI32_Size(Dqn_RectI32 rect) +{ + Dqn_V2I result = rect.max - rect.min; + return result; +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Math Utils +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_V2 Dqn_LerpV2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b) +{ + Dqn_V2 result = {}; + result.x = a.x + ((b.x - a.x) * t); + result.y = a.y + ((b.y - a.y) * t); + return result; +} + +DQN_API Dqn_f32 Dqn_LerpF32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b) +{ + Dqn_f32 result = a + ((b - a) * t); + return result; +} + + // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_Bit @@ -4727,7 +5220,7 @@ DQN_API Dqn_FileInfo Dqn_File_Info(char const *path) } #else // TODO(doyle): Implement - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_INVALID_CODE_PATH; #endif return result; @@ -4757,25 +5250,7 @@ DQN_API Dqn_isize Dqn_EpochTimeToLocalDate(Dqn_i64 timestamp, Dqn_EpochTimeForma return result; } -DQN_API Dqn_Timer Dqn_Timer_Begin() -{ -#if defined(DQN_OS_WIN32) - Dqn_Timer result = {}; - BOOL qpc_result = QueryPerformanceCounter(&result.start); - (void)qpc_result; - DQN_ASSERT_MSG(qpc_result, "MSDN says this can only fail when running on a version older than Windows XP"); -#endif - return result; -} - -DQN_API void Dqn_Timer_End(Dqn_Timer *timer) -{ -#if defined(DQN_OS_WIN32) - QueryPerformanceCounter(&timer->end); -#endif -} - -DQN_FILE_SCOPE void Dqn_Timer__Init() +DQN_FILE_SCOPE void Dqn_PerfCounter__Init() { #if defined(DQN_OS_WIN32) if (dqn__lib.win32_qpc_frequency.QuadPart == 0) @@ -4783,46 +5258,97 @@ DQN_FILE_SCOPE void Dqn_Timer__Init() #endif } -DQN_API Dqn_u64 Dqn_Timer_S(Dqn_Timer const *timer) +DQN_API Dqn_f64 Dqn_PerfCounter_S(Dqn_u64 begin, Dqn_u64 end) { - Dqn_Timer__Init(); + Dqn_PerfCounter__Init(); #if defined(DQN_OS_WIN32) - Dqn_u64 ticks = timer->end.QuadPart - timer->start.QuadPart; - Dqn_u64 result = ticks / dqn__lib.win32_qpc_frequency.QuadPart; + Dqn_u64 ticks = end - begin; + Dqn_f64 result = ticks / DQN_CAST(Dqn_f64)dqn__lib.win32_qpc_frequency.QuadPart; #endif return result; } -DQN_API Dqn_u64 Dqn_Timer_Ms(Dqn_Timer const *timer) +DQN_API Dqn_f64 Dqn_PerfCounter_Ms(Dqn_u64 begin, Dqn_u64 end) { - Dqn_Timer__Init(); + Dqn_PerfCounter__Init(); #if defined(DQN_OS_WIN32) - Dqn_u64 ticks = timer->end.QuadPart - timer->start.QuadPart; - Dqn_u64 result = (ticks * 1000) / dqn__lib.win32_qpc_frequency.QuadPart; + Dqn_u64 ticks = end - begin; + Dqn_f64 result = (ticks * 1'000) / DQN_CAST(Dqn_f64)dqn__lib.win32_qpc_frequency.QuadPart; #endif return result; } -DQN_API Dqn_u64 Dqn_Timer_MicroS(Dqn_Timer const *timer) +DQN_API Dqn_f64 Dqn_PerfCounter_MicroS(Dqn_u64 begin, Dqn_u64 end) { - Dqn_Timer__Init(); + Dqn_PerfCounter__Init(); #if defined(DQN_OS_WIN32) - Dqn_u64 ticks = timer->end.QuadPart - timer->start.QuadPart; - Dqn_u64 result = (ticks * 1000000) / dqn__lib.win32_qpc_frequency.QuadPart; + Dqn_u64 ticks = end - begin; + Dqn_f64 result = (ticks * 1'000'000) / DQN_CAST(Dqn_f64)dqn__lib.win32_qpc_frequency.QuadPart; #endif return result; } -DQN_API Dqn_u64 Dqn_Timer_Ns(Dqn_Timer const *timer) +DQN_API Dqn_f64 Dqn_PerfCounter_Ns(Dqn_u64 begin, Dqn_u64 end) { - Dqn_Timer__Init(); + Dqn_PerfCounter__Init(); #if defined(DQN_OS_WIN32) - Dqn_u64 ticks = timer->end.QuadPart - timer->start.QuadPart; - Dqn_u64 result = (ticks * 1000000000) / dqn__lib.win32_qpc_frequency.QuadPart; + Dqn_u64 ticks = end - begin; + Dqn_f64 result = (ticks * 1'000'000'000) / DQN_CAST(Dqn_f64)dqn__lib.win32_qpc_frequency.QuadPart; #endif return result; } +DQN_API Dqn_u64 Dqn_PerfCounter_Now() +{ + Dqn_u64 result = 0; +#if defined(DQN_OS_WIN32) + LARGE_INTEGER integer = {}; + BOOL qpc_result = QueryPerformanceCounter(&integer); + (void)qpc_result; + DQN_ASSERT_MSG(qpc_result, "MSDN says this can only fail when running on a version older than Windows XP"); + result = integer.QuadPart; +#endif + + DQN_ASSERT_MSG(result != 0, "Function not implemented"); + return result; +} + +DQN_API Dqn_Timer Dqn_Timer_Begin() +{ + Dqn_Timer result = {}; + result.start = Dqn_PerfCounter_Now(); + return result; +} + +DQN_API void Dqn_Timer_End(Dqn_Timer *timer) +{ + timer->end = Dqn_PerfCounter_Now(); +} + +DQN_API Dqn_f64 Dqn_Timer_S(Dqn_Timer timer) +{ + Dqn_f64 result = Dqn_PerfCounter_S(timer.start, timer.end); + return result; +} + +DQN_API Dqn_f64 Dqn_Timer_Ms(Dqn_Timer timer) +{ + Dqn_f64 result = Dqn_PerfCounter_Ms(timer.start, timer.end); + return result; +} + +DQN_API Dqn_f64 Dqn_Timer_MicroS(Dqn_Timer timer) +{ + Dqn_f64 result = Dqn_PerfCounter_MicroS(timer.start, timer.end); + return result; +} + +DQN_API Dqn_f64 Dqn_Timer_Ns(Dqn_Timer timer) +{ + Dqn_f64 result = Dqn_PerfCounter_Ns(timer.start, timer.end); + return result; +} + DQN_API char *Dqn_U64ToStr(Dqn_u64 val, Dqn_U64Str *result, Dqn_b32 comma_sep) { int buf_index = (int)(Dqn_ArrayCount(result->buf) - 1); @@ -4861,6 +5387,52 @@ DQN_API char *Dqn_U64ToTempStr(Dqn_u64 val, Dqn_b32 comma_sep) return result; } +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_Win32 Implementation +// +// ------------------------------------------------------------------------------------------------- +DQN_API Dqn_FixedString<1024> Dqn_Win32_LastError() +{ + Dqn_FixedString<1024> result = {}; + Dqn_FixedString_AppendFmt(&result, "(%d) ", GetLastError()); + result.size += FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + result.str + result.size, + DQN_CAST(DWORD)(sizeof(result.str) - result.size), + nullptr); + return result; +} + +DQN_API wchar_t *Dqn_Win32_ArenaToWChar(Dqn_ArenaAllocator *arena, Dqn_String src, int *wchar_size) +{ + int size_int = Dqn_Safe_TruncateISizeToInt(src.size); + int required = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.str, size_int, nullptr, 0); + if (required == 0) + { + DQN_LOG_W("Failed to convert string '%.*s' to wide string: '%.*s'", + DQN_STRING_FMT(src), + Dqn_Win32_LastError().str); + return nullptr; + } + + wchar_t *result = Dqn_ArenaAllocator_NewArray(arena, wchar_t, required + 1, Dqn_ZeroMem::No); + if (result) + { + *wchar_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.str, size_int, result, required); + DQN_HARD_ASSERT_MSG(*wchar_size != 0, "Error should be handled in the case above"); + result[required] = 0; + DQN_ASSERT_MSG(*wchar_size == required, "Sanity check this API"); + DQN_ASSERT_MSG(result[required - 1] != 0, + "Sanity check that str[required] was actually the byte that you have to null-terminate, i.e. " + "the character before is not a null character."); + } + + return result; +} + // ------------------------------------------------------------------------------------------------- // // NOTE: Hashing - Dqn_FNV1A[32|64] @@ -4903,7 +5475,7 @@ DQN_API Dqn_u64 Dqn_FNV1A64_Hash(void const *bytes, Dqn_isize size) // // ------------------------------------------------------------------------------------------------- -#if defined(DQN_COMPILER_MSVC) +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #define DQN_MMH3_FORCE_INLINE __forceinline #define DQN_MMH3_ROTL32(x, y) _rotl(x, y) #define DQN_MMH3_ROTL64(x, y) _rotl64(x, y) @@ -6777,7 +7349,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ -#if defined(DQN_COMPILER_MSVC) +#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #if !defined(DQN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED) #undef _CRT_SECURE_NO_WARNINGS #endif diff --git a/Code/Dqn_UnitTests.cpp b/Code/Dqn_Tests.cpp similarity index 50% rename from Code/Dqn_UnitTests.cpp rename to Code/Dqn_Tests.cpp index 72d0dac..7b1c40d 100644 --- a/Code/Dqn_UnitTests.cpp +++ b/Code/Dqn_Tests.cpp @@ -1,9 +1,20 @@ -#define DQN_USE_PRIMITIVE_TYPEDEFS -#define DQN_NO_ASSERT -#define DQN_IMPLEMENTATION -#include "Dqn.h" +// ------------------------------------------------------------------------------------------------- +// +// 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_NO_ANSI_COLORS Define this to disable any ANSI terminal color codes from output +*/ -struct TestState +#if defined(DQN_TEST_WITH_MAIN) + #define DQN_IMPLEMENTATION + #include "Dqn.h" +#endif + +struct Dqn_TestState { int indent_level; Dqn_String name; @@ -12,143 +23,160 @@ struct TestState bool scope_started; }; -struct TestingState +struct Dqn_TestingState { int num_tests_in_group; int num_tests_ok_in_group; - TestState test; - Dqn_ArenaAllocator arena_; - Dqn_Allocator allocator; + Dqn_TestState test; + Dqn_ArenaAllocator arena; }; -#define ANSI_COLOR_RED "\x1b[31m" -#define ANSI_COLOR_GREEN "\x1b[32m" -#define ANSI_COLOR_YELLOW "\x1b[33m" -#define ANSI_COLOR_BLUE "\x1b[34m" -#define ANSI_COLOR_MAGENTA "\x1b[35m" -#define ANSI_COLOR_CYAN "\x1b[36m" -#define ANSI_COLOR_RESET "\x1b[0m" +#if defined(DQN_TEST_NO_ANSI_COLORS) + #define DQN_TEST_ANSI_COLOR_RED + #define DQN_TEST_ANSI_COLOR_GREEN + #define DQN_TEST_ANSI_COLOR_YELLOW + #define DQN_TEST_ANSI_COLOR_BLUE + #define DQN_TEST_ANSI_COLOR_MAGENTA + #define DQN_TEST_ANSI_COLOR_CYAN + #define DQN_TEST_ANSI_COLOR_RESET +#else + #define DQN_TEST_ANSI_COLOR_RED "\x1b[31m" + #define DQN_TEST_ANSI_COLOR_GREEN "\x1b[32m" + #define DQN_TEST_ANSI_COLOR_YELLOW "\x1b[33m" + #define DQN_TEST_ANSI_COLOR_BLUE "\x1b[34m" + #define DQN_TEST_ANSI_COLOR_MAGENTA "\x1b[35m" + #define DQN_TEST_ANSI_COLOR_CYAN "\x1b[36m" + #define DQN_TEST_ANSI_COLOR_RESET "\x1b[0m" +#endif -#define TEST_START_SCOPE(testing_state, test_name) \ +#define DQN_TEST_START_SCOPE(testing_state, test_name) \ DQN_DEFER \ { \ if (testing_state.test.fail_expr.size == 0) testing_state.num_tests_ok_in_group++; \ - TestState_PrintResult(&testing_state.test); \ - Dqn_ArenaAllocator_ResetUsage(&testing_state.arena_, Dqn_ZeroMem::No); \ - testing_state.allocator = Dqn_Allocator_InitWithArena(&testing_state.arena_); \ - testing_state.test = {}; \ + Dqn_TestState_PrintResult(&testing_state.test); \ + Dqn_ArenaAllocator_ResetUsage(&testing_state.arena, Dqn_ZeroMem::No); \ + testing_state.test = {}; \ }; \ - testing_state.test.name = Dqn_String_InitFmt(&testing_state.allocator, test_name); \ + testing_state.test.name = DQN_STRING(test_name); \ testing_state.test.scope_started = true; \ testing_state.num_tests_in_group++ -#define TEST_DECLARE_GROUP_SCOPED(testing_state, name) \ - fprintf(stdout, "\n" name "\n"); \ +// NOTE: Zero initialised allocators can be a null allocator if #define +// DQN_ALLOCATOR_DEFAULT_TO_NULL is defined, so handle this case specially +// by defaulting to the heap allocator which is the behaviour it would have +// used if the hash define was not used. + +// In the macro below we ensure that the allocator is not null, this idiom is +// repeated whereever we zero initialise an allocator. + +#define DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, name) \ + fprintf(stdout, name "\n"); \ + if (testing_state.arena.backup_allocator.type == Dqn_AllocatorType::Null) \ + testing_state.arena.backup_allocator = Dqn_Allocator_InitWithHeap(); \ DQN_DEFER \ { \ - TestingState_PrintGroupResult(&testing_state); \ + Dqn_TestingState_PrintGroupResult(&testing_state); \ testing_state = {}; \ + fprintf(stdout, "\n\n"); \ } -#define TEST_EXPECT_MSG(testing_state, expr, msg, ...) \ +#define DQN_TEST_EXPECT_MSG(testing_state, expr, msg, ...) \ DQN_ASSERT(testing_state.test.scope_started); \ if (!(expr)) \ { \ - testing_state.test.fail_expr = Dqn_String_InitFmt(&testing_state.allocator, #expr); \ - testing_state.test.fail_msg = Dqn_String_InitFmt(&testing_state.allocator, msg, ##__VA_ARGS__); \ + testing_state.test.fail_expr = DQN_STRING(#expr); \ + testing_state.test.fail_msg = Dqn_String_InitArenaFmt(&testing_state.arena, msg, ##__VA_ARGS__); \ } -#define TEST_EXPECT(testing_state, expr) TEST_EXPECT_MSG(testing_state, expr, "") +#define DQN_TEST_EXPECT(testing_state, expr) DQN_TEST_EXPECT_MSG(testing_state, expr, "") -void TestingState_PrintGroupResult(TestingState const *result) +void Dqn_TestingState_PrintGroupResult(Dqn_TestingState const *result) { - char constexpr INDENT[] = " "; - int constexpr DESIRED_LEN = 72; - char const STATUS_OK[] = "OK"; - char const STATUS_FAIL[] = "FAIL"; + int const DESIRED_LEN = 72; + char const STATUS_OK[] = "OK"; + char const STATUS_FAIL[] = "FAIL"; bool all_tests_passed = (result->num_tests_ok_in_group == result->num_tests_in_group); char buf[256] = {}; int size = snprintf(buf, Dqn_ArrayCount(buf), "%02d/%02d Tests Passed ", result->num_tests_ok_in_group, result->num_tests_in_group); Dqn_isize remaining_size = DESIRED_LEN - size; remaining_size = (all_tests_passed) ? remaining_size - Dqn_CharCount(STATUS_OK) : remaining_size - Dqn_CharCount(STATUS_FAIL); - remaining_size = DQN_MAX(remaining_size, 0); + remaining_size = DQN_M_MAX(remaining_size, 0); DQN_FOR_EACH(i, remaining_size) fprintf(stdout, " "); fprintf(stdout, "%s", buf); if (result->num_tests_ok_in_group == result->num_tests_in_group) - fprintf(stdout, ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET, STATUS_OK); + fprintf(stdout, DQN_TEST_ANSI_COLOR_GREEN "%s" DQN_TEST_ANSI_COLOR_RESET, STATUS_OK); else - fprintf(stdout, ANSI_COLOR_RED "%s" ANSI_COLOR_RESET, STATUS_FAIL); + fprintf(stdout, DQN_TEST_ANSI_COLOR_RED "%s" DQN_TEST_ANSI_COLOR_RESET, STATUS_FAIL); } -void TestState_PrintResult(TestState const *result) +void Dqn_TestState_PrintResult(Dqn_TestState const *result) { - char constexpr INDENT[] = " "; - int constexpr DESIRED_LEN = 72; + char const INDENT[] = " "; + int const DESIRED_LEN = 72; fprintf(stdout, "%s%s", INDENT, result->name.str); char const STATUS_OK[] = "OK"; char const STATUS_FAIL[] = "FAIL"; Dqn_isize remaining_size = DESIRED_LEN - result->name.size - Dqn_CharCount(INDENT); - remaining_size = (result->fail_expr.str) ? remaining_size - Dqn_CharCount(STATUS_FAIL) : remaining_size - Dqn_CharCount(STATUS_OK); - remaining_size = DQN_MAX(remaining_size, 0); + remaining_size = (result->fail_expr.str) ? remaining_size - Dqn_CharCount(STATUS_FAIL) : remaining_size - Dqn_CharCount(STATUS_OK); + remaining_size = DQN_M_MAX(remaining_size, 0); DQN_FOR_EACH(i, remaining_size) fprintf(stdout, "."); if (result->fail_expr.str) { - fprintf(stdout, ANSI_COLOR_RED "%s" ANSI_COLOR_RESET "\n", STATUS_FAIL); + fprintf(stdout, DQN_TEST_ANSI_COLOR_RED "%s" DQN_TEST_ANSI_COLOR_RESET "\n", STATUS_FAIL); fprintf(stdout, "%s%sReason: Expression failed (%s) %s\n", INDENT, INDENT, result->fail_expr.str, result->fail_msg.str); } else { - fprintf(stdout, ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET "\n", STATUS_OK); + fprintf(stdout, DQN_TEST_ANSI_COLOR_GREEN "%s" DQN_TEST_ANSI_COLOR_RESET "\n", STATUS_OK); } } -static void UnitTests() +static void Dqn_Test_UnitTests() { - TestingState testing_state = {}; + Dqn_TestingState testing_state = {}; + // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_Allocator - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); // NOTE: Various allocator test { { - TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); + DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - char constexpr EXPECT[] = "hello_world"; + 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)); - TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } { - TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); + DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - char constexpr EXPECT[] = "hello_world"; + 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)); - TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } { - TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); + 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 constexpr EXPECT[] = "hello_world"; + 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)); - TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); + DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); } } @@ -157,30 +185,33 @@ static void UnitTests() Dqn_u8 const ALIGNMENT3 = 4; Dqn_u8 const NUM_BYTES = sizeof(Dqn_u32); { - TEST_START_SCOPE(testing_state, "HeapAllocator - Align to 32 bytes"); + 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); - TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); } { - TEST_START_SCOPE(testing_state, "XHeapAllocator - Align to 32 bytes"); + 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); - TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); } { - TEST_START_SCOPE(testing_state, "ArenaAllocator - Align to 32 bytes"); - Dqn_ArenaAllocator arena = {}; + 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); - TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); + DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); } } @@ -190,121 +221,119 @@ static void UnitTests() Dqn_u8 const NUM_BYTES = 4; Dqn_u8 const MAX_OFFSET = (ALIGNMENT3 - 1) + sizeof(Dqn_PointerMetadata); { - TEST_START_SCOPE(testing_state, "HeapAllocator - Allocation metadata initialised"); + 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); - TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - 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, 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); } { - TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocation metadata initialised"); + 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); - TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - 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, 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); } } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_Array - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); // NOTE: Dqn_Array_InitWithMemory { { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more"); + 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); - TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); - TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data %d", array.data[3]); - TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); + 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); - TEST_EXPECT(testing_state, added_item == nullptr); - TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); - TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + 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); } { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items"); + 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)); - TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); - TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); - 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", 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); } { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed"); + 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); - TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); - TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); - 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_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); - 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, array.data[0] == 0, "array.data %d. Clear but zero memory old values should not remain", array.data[0]); } { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable"); + 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); - TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.data[1] == 4, "array.data %d", array.data[1]); - TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); - TEST_EXPECT_MSG(testing_state, array.size == 3, "array.size: %d", array.size); + 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); - TEST_EXPECT_MSG(testing_state, array.data[0] == 4, "array.data: %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.data[1] == 3, "array.data: %d", array.data[1]); - TEST_EXPECT_MSG(testing_state, array.size == 2, "array.size: %d", array.size); + 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); } { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek"); + 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); - TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); - 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", 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); - TEST_EXPECT_MSG(testing_state, *peek_item == 1, "peek: %d", *peek_item); - TEST_EXPECT_MSG(testing_state, array.size == 1, "array.size: %d", array.size); - TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + 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); } { - TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing"); + 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); }; @@ -314,47 +343,48 @@ static void UnitTests() // NOTE: Dynamic Memory: Dqn_Array { { - TEST_START_SCOPE(testing_state, "Dynamic Memory: Test reserve and over commit reallocates"); + 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_DEFER { Dqn_Array_Free(&array); }; Dqn_Array_Reserve(&array, 4); - TEST_EXPECT_MSG(testing_state, array.size == 0, "array.size: %d", array.size); - TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + 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)); - TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); - TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data: %d", array.data[1]); - TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data: %d", array.data[2]); - TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data: %d", array.data[3]); - TEST_EXPECT_MSG(testing_state, array.size == 4, "array.size: %d", array.size); + 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); - TEST_EXPECT_MSG(testing_state, *added_item == 5, "added_item: %d", *added_item); - TEST_EXPECT_MSG(testing_state, array.data[4] == 5, "array.data: %d", array.data[4]); - TEST_EXPECT_MSG(testing_state, array.size == 5, "array.size: %d", array.size); - TEST_EXPECT_MSG(testing_state, array.max >= 5, "array.max: %d", array.max); + 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_Rect - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Rect"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Rect"); // NOTE: Dqn_Rect_Intersection { { - TEST_START_SCOPE(testing_state, "No 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -364,12 +394,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "A's min intersects B"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -379,12 +409,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "B's min intersects A"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -394,12 +424,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "A's max intersects B"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -409,12 +439,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "B's max intersects A"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -425,12 +455,12 @@ static void UnitTests() { - TEST_START_SCOPE(testing_state, "B contains A"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -440,12 +470,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "A contains B"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -455,12 +485,12 @@ static void UnitTests() } { - TEST_START_SCOPE(testing_state, "A equals B"); + 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); - TEST_EXPECT_MSG(testing_state, + 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, @@ -472,73 +502,86 @@ static void UnitTests() } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_StringBuilder - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder"); Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); // NOTE: Dqn_StringBuilder_Append { { - TEST_START_SCOPE(testing_state, "Append variable size strings and build using heap allocator"); + 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_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); }; - char constexpr EXPECT_STR[] = "Acd"; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } { - TEST_START_SCOPE(testing_state, "Append empty string and build using heap allocator"); + 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 constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } { - TEST_START_SCOPE(testing_state, "Append empty string onto string and build using heap allocator"); + 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 constexpr EXPECT_STR[] = "Acd"; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } { - TEST_START_SCOPE(testing_state, "Append nullptr and build using heap allocator"); + 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 constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } { - TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using heap allocator"); + 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"); @@ -546,296 +589,323 @@ static void UnitTests() char *result = Dqn_StringBuilder_Build(&builder, &allocator, &size); DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; - char constexpr EXPECT_STR[] = "Aztec"; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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 { - TEST_START_SCOPE(testing_state, "Append char and build using heap allocator"); + 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 constexpr EXPECT_STR[] = "ab"; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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 { { - TEST_START_SCOPE(testing_state, "Append format string and build using heap allocator"); + 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 constexpr EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff"; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } { - TEST_START_SCOPE(testing_state, "Append nullptr format string and build using heap allocator"); + 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 constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size); - TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", 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); } } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_FixedArray - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedArray"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedArray"); // NOTE: Dqn_FixedArray_Init { - TEST_START_SCOPE(testing_state, "Initialise from raw array"); + 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)); - TEST_EXPECT(testing_state, array.size == 2); - TEST_EXPECT(testing_state, array[0] == 1); - TEST_EXPECT(testing_state, array[1] == 2); + 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 { - TEST_START_SCOPE(testing_state, "Erase stable 1 element from array"); + 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); - TEST_EXPECT(testing_state, array.size == 2); - TEST_EXPECT(testing_state, array[0] == 1); - TEST_EXPECT(testing_state, array[1] == 3); + 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 { - TEST_START_SCOPE(testing_state, "Erase unstable 1 element from array"); + 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); - TEST_EXPECT(testing_state, array.size == 2); - TEST_EXPECT(testing_state, array[0] == 3); - TEST_EXPECT(testing_state, array[1] == 2); + 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 { - TEST_START_SCOPE(testing_state, "Add 1 element to array"); + 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); - TEST_EXPECT(testing_state, array.size == 2); - TEST_EXPECT(testing_state, array[0] == 1); - TEST_EXPECT(testing_state, array[1] == 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 { - TEST_START_SCOPE(testing_state, "Clear array"); + 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); - TEST_EXPECT(testing_state, array.size == 0); + DQN_TEST_EXPECT(testing_state, array.size == 0); } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_FixedString - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedString"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedString"); // NOTE: Dqn_FixedString_Append { - TEST_START_SCOPE(testing_state, "Append too much fails"); + DQN_TEST_START_SCOPE(testing_state, "Append too much fails"); Dqn_FixedString<4> str = {}; - TEST_EXPECT_MSG(testing_state, Dqn_FixedString_Append(&str, "abcd") == false, "We need space for the null-terminator"); + DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_Append(&str, "abcd") == false, "We need space for the null-terminator"); } // NOTE: Dqn_FixedString_AppendFmt { - TEST_START_SCOPE(testing_state, "Append format string too much fails"); + DQN_TEST_START_SCOPE(testing_state, "Append format string too much fails"); Dqn_FixedString<4> str = {}; - TEST_EXPECT_MSG(testing_state, Dqn_FixedString_AppendFmt(&str, "abcd") == false, "We need space for the null-terminator"); + DQN_TEST_EXPECT_MSG(testing_state, Dqn_FixedString_AppendFmt(&str, "abcd") == false, "We need space for the null-terminator"); } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_Str_ToI64 - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToI64"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToI64"); { - TEST_START_SCOPE(testing_state, "Convert nullptr"); + DQN_TEST_START_SCOPE(testing_state, "Convert nullptr"); Dqn_i64 result = Dqn_Str_ToI64(nullptr); - TEST_EXPECT(testing_state, result == 0); + DQN_TEST_EXPECT(testing_state, result == 0); } { - TEST_START_SCOPE(testing_state, "Convert empty string"); + DQN_TEST_START_SCOPE(testing_state, "Convert empty string"); Dqn_i64 result = Dqn_Str_ToI64(""); - TEST_EXPECT(testing_state, result == 0); + DQN_TEST_EXPECT(testing_state, result == 0); } { - TEST_START_SCOPE(testing_state, "Convert \"1\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1\""); Dqn_i64 result = Dqn_Str_ToI64("1"); - TEST_EXPECT(testing_state, result == 1); + DQN_TEST_EXPECT(testing_state, result == 1); } { - TEST_START_SCOPE(testing_state, "Convert \"-0\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"-0\""); Dqn_i64 result = Dqn_Str_ToI64("-0"); - TEST_EXPECT(testing_state, result == 0); + DQN_TEST_EXPECT(testing_state, result == 0); } { - TEST_START_SCOPE(testing_state, "Convert \"-1\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"-1\""); Dqn_i64 result = Dqn_Str_ToI64("-1"); - TEST_EXPECT(testing_state, result == -1); + DQN_TEST_EXPECT(testing_state, result == -1); } { - TEST_START_SCOPE(testing_state, "Convert \"1.2\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1.2\""); Dqn_i64 result = Dqn_Str_ToI64("1.2"); - TEST_EXPECT(testing_state, result == 1); + DQN_TEST_EXPECT(testing_state, result == 1); } { - TEST_START_SCOPE(testing_state, "Convert \"1,234\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1,234\""); Dqn_i64 result = Dqn_Str_ToI64("1,234"); - TEST_EXPECT(testing_state, result == 1234); + DQN_TEST_EXPECT(testing_state, result == 1234); } { - TEST_START_SCOPE(testing_state, "Convert \"1,2\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1,2\""); Dqn_i64 result = Dqn_Str_ToI64("1,2"); - TEST_EXPECT(testing_state, result == 12); + DQN_TEST_EXPECT(testing_state, result == 12); } { - TEST_START_SCOPE(testing_state, "Convert \"12a3\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"12a3\""); Dqn_i64 result = Dqn_Str_ToI64("12a3"); - TEST_EXPECT(testing_state, result == 12); + DQN_TEST_EXPECT(testing_state, result == 12); } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_Str_ToU64 - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToU64"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToU64"); { - TEST_START_SCOPE(testing_state, "Convert nullptr"); + DQN_TEST_START_SCOPE(testing_state, "Convert nullptr"); Dqn_u64 result = Dqn_Str_ToU64(nullptr); - TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert empty string"); + DQN_TEST_START_SCOPE(testing_state, "Convert empty string"); Dqn_u64 result = Dqn_Str_ToU64(""); - TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"1\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1\""); Dqn_u64 result = Dqn_Str_ToU64("1"); - TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"-0\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"-0\""); Dqn_u64 result = Dqn_Str_ToU64("-0"); - TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"-1\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"-1\""); Dqn_u64 result = Dqn_Str_ToU64("-1"); - TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"1.2\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1.2\""); Dqn_u64 result = Dqn_Str_ToU64("1.2"); - TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"1,234\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1,234\""); Dqn_u64 result = Dqn_Str_ToU64("1,234"); - TEST_EXPECT_MSG(testing_state, result == 1234, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 1234, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"1,2\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"1,2\""); Dqn_u64 result = Dqn_Str_ToU64("1,2"); - TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); } { - TEST_START_SCOPE(testing_state, "Convert \"12a3\""); + DQN_TEST_START_SCOPE(testing_state, "Convert \"12a3\""); Dqn_u64 result = Dqn_Str_ToU64("12a3"); - TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); + DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); } } // --------------------------------------------------------------------------------------------- - // // NOTE: Dqn_Str_Find - // // --------------------------------------------------------------------------------------------- { - TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_Find"); + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_Find"); { - TEST_START_SCOPE(testing_state, "String (char) is not in buffer"); + DQN_TEST_START_SCOPE(testing_state, "String (char) is not in buffer"); char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; char const find[] = "2"; char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); - TEST_EXPECT(testing_state, result == nullptr); + DQN_TEST_EXPECT(testing_state, result == nullptr); } { - TEST_START_SCOPE(testing_state, "String (char) is in buffer"); + DQN_TEST_START_SCOPE(testing_state, "String (char) is in buffer"); char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; char const find[] = "6"; char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); - TEST_EXPECT(testing_state, result != nullptr); - TEST_EXPECT(testing_state, result[0] == '6' && result[1] == 'a'); + DQN_TEST_EXPECT(testing_state, result != nullptr); + DQN_TEST_EXPECT(testing_state, result[0] == '6' && result[1] == 'a'); + } + } + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_M4 + // --------------------------------------------------------------------------------------------- + { + 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); } } } +#if defined(DQN_TEST_WITH_MAIN) int main(int argc, char *argv[]) { (void)argv; (void)argc; - UnitTests(); + Dqn_Test_UnitTests(); return 0; } +#endif diff --git a/Code/build.sh b/Code/build.sh index 1023be7..a0f32d6 100755 --- a/Code/build.sh +++ b/Code/build.sh @@ -1,4 +1,3 @@ mkdir -p ../Bin/ pushd ../Bin/ -g++ ../Code/Dqn_UnitTests.cpp -std=c++17 -o Dqn_UnitTests -g++ ../Code/DqnHeader.h -D DQN_HEADER_IMPLEMENTATION -std=c++17 -o DqnHeader +g++ ../Code/Dqn_Tests.cpp -D DQN_TEST_WITH_MAIN -std=c++17 -o Dqn_UnitTests diff --git a/Project/dqn.rdbg b/Project/dqn.rdbg index 0198314..b890f6c 100644 Binary files a/Project/dqn.rdbg and b/Project/dqn.rdbg differ