dqn: Simplify testing routine
This commit is contained in:
parent
d9116ae48e
commit
44f7a696c5
316
dqn.h
316
dqn.h
@ -57,6 +57,10 @@
|
|||||||
// #define DQN_NO_ASSERT
|
// #define DQN_NO_ASSERT
|
||||||
// Turn all assertion macros to no-ops
|
// Turn all assertion macros to no-ops
|
||||||
//
|
//
|
||||||
|
// #define DQN_NO_CHECK_BREAK
|
||||||
|
// Disable debug break when a check macro's expression fails. Instead only
|
||||||
|
// the error will be logged.
|
||||||
|
//
|
||||||
// #define DQN_NO_WIN32_MINIMAL_HEADER
|
// #define DQN_NO_WIN32_MINIMAL_HEADER
|
||||||
// Define this to stop this library from defining a minimal subset of Win32
|
// Define this to stop this library from defining a minimal subset of Win32
|
||||||
// prototypes and definitions in this file. Useful for stopping redefinition
|
// prototypes and definitions in this file. Useful for stopping redefinition
|
||||||
@ -283,6 +287,30 @@
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DQN_CHECK(expr) DQN_CHECKF(expr, "")
|
||||||
|
#if defined(DQN_NO_CHECK_BREAK)
|
||||||
|
#define DQN_CHECKF(expr, fmt, ...) \
|
||||||
|
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__), false))
|
||||||
|
#else
|
||||||
|
#define DQN_CHECKF(expr, fmt, ...) \
|
||||||
|
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__), DQN_DEBUG_BREAK, false))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
DQN_API bool DQN_CHECKF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
bool result = assertion_expr;
|
||||||
|
if (!result) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, call_site, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
DQN_DEBUG_BREAK;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
#define DQN_ZERO_INIT {}
|
#define DQN_ZERO_INIT {}
|
||||||
#else
|
#else
|
||||||
@ -1310,6 +1338,7 @@ typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_Ca
|
|||||||
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args)
|
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||||
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
DQN_API Dqn_String8 Dqn_Log_MakeString (Dqn_Allocator allocator, bool colour, Dqn_String8 type, int log_type, Dqn_CallSite call_site, char const *fmt, va_list args);
|
||||||
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||||
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...);
|
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...);
|
||||||
DQN_API void Dqn_Log_FVCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
DQN_API void Dqn_Log_FVCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||||
@ -1488,7 +1517,7 @@ struct Dqn_ArenaStatString
|
|||||||
struct Dqn_Arena
|
struct Dqn_Arena
|
||||||
{
|
{
|
||||||
bool use_after_free_guard;
|
bool use_after_free_guard;
|
||||||
|
uint32_t temp_memory_count;
|
||||||
Dqn_String8 label; ///< Optional label to describe the arena
|
Dqn_String8 label; ///< Optional label to describe the arena
|
||||||
Dqn_ArenaBlock *head; ///< Active block the arena is allocating from
|
Dqn_ArenaBlock *head; ///< Active block the arena is allocating from
|
||||||
Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
|
Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
|
||||||
@ -2424,26 +2453,17 @@ DQN_API bool Dqn_Bit_IsNotSet(uint32_t bits, uint32_t bits_to_check);
|
|||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
#if defined(NDEBUG)
|
|
||||||
#define Dqn_Safe_AssertF(expr, fmt, ...)
|
|
||||||
#define Dqn_Safe_Assert(expr, fmt, ...)
|
|
||||||
#else
|
|
||||||
#define Dqn_Safe_AssertF(expr, fmt, ...) \
|
|
||||||
Dqn_Safe_AssertF_(expr, DQN_CALL_SITE, "Safe assert triggered " #expr ": " fmt, ## __VA_ARGS__)
|
|
||||||
#define Dqn_Safe_Assert(expr) Dqn_Safe_AssertF(expr, "")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Assert the expression given in debug, whilst in release- assertion is
|
/// Assert the expression given in debug, whilst in release- assertion is
|
||||||
/// removed and the expression is evaluated and returned.
|
/// removed and the expression is evaluated and returned.
|
||||||
///
|
///
|
||||||
/// This function provides dual logic which allows handling of the condition
|
/// This function provides dual logic which allows handling of the condition
|
||||||
/// gracefully in release mode, but asserting in debug mode. This is an internal
|
/// gracefully in release mode, but asserting in debug mode. This is an internal
|
||||||
/// function, prefer the @see Dqn_Safe_Assert macros.
|
/// function, prefer the @see DQN_CHECK macros.
|
||||||
///
|
///
|
||||||
/// @param assertion_expr[in] Expressin to assert on
|
/// @param assertion_expr[in] Expressin to assert on
|
||||||
/// @param fmt[in] Format string for providing a message on assertion
|
/// @param fmt[in] Format string for providing a message on assertion
|
||||||
/// @return True if the expression evaluated to true, false otherwise.
|
/// @return True if the expression evaluated to true, false otherwise.
|
||||||
DQN_API bool Dqn_Safe_AssertF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...);
|
DQN_API bool DQN_CHECKF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...);
|
||||||
|
|
||||||
// NOTE: Dqn_Safe Arithmetic
|
// NOTE: Dqn_Safe Arithmetic
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -3606,7 +3626,7 @@ DQN_API template <typename T> T *Dqn_VArray_Make(Dqn_VArray<T> *array, Dqn_usize
|
|||||||
if (!Dqn_VArray_IsValid(array))
|
if (!Dqn_VArray_IsValid(array))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!Dqn_Safe_AssertF((array->size + count) < array->max, "Array is out of virtual memory"))
|
if (!DQN_CHECKF((array->size + count) < array->max, "Array is out of virtual memory"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// TODO: Use placement new? Why doesn't this work?
|
// TODO: Use placement new? Why doesn't this work?
|
||||||
@ -3696,7 +3716,7 @@ template <typename T>
|
|||||||
Dqn_DSMap<T> Dqn_DSMap_Init(uint32_t size)
|
Dqn_DSMap<T> Dqn_DSMap_Init(uint32_t size)
|
||||||
{
|
{
|
||||||
Dqn_DSMap<T> result = {};
|
Dqn_DSMap<T> result = {};
|
||||||
if (Dqn_Safe_AssertF((size & (size - 1)) == 0, "Power-of-two size required")) {
|
if (DQN_CHECKF((size & (size - 1)) == 0, "Power-of-two size required")) {
|
||||||
result.hash_to_slot = Dqn_Allocator_NewArray(result.allocator, uint32_t, size, Dqn_ZeroMem_Yes);
|
result.hash_to_slot = Dqn_Allocator_NewArray(result.allocator, uint32_t, size, Dqn_ZeroMem_Yes);
|
||||||
if (result.hash_to_slot) {
|
if (result.hash_to_slot) {
|
||||||
result.slots = Dqn_Allocator_NewArray(result.allocator, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_Yes);
|
result.slots = Dqn_Allocator_NewArray(result.allocator, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_Yes);
|
||||||
@ -4200,7 +4220,7 @@ DQN_API template <typename T, Dqn_usize N> T *Dqn_FArray_Make(Dqn_FArray<T, N> *
|
|||||||
if (!Dqn_FArray_IsValid(array))
|
if (!Dqn_FArray_IsValid(array))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!Dqn_Safe_AssertF((array->size + count) < DQN_ARRAY_UCOUNT(array->data), "Array is out of memory"))
|
if (!DQN_CHECKF((array->size + count) < DQN_ARRAY_UCOUNT(array->data), "Array is out of memory"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// TODO: Use placement new? Why doesn't this work?
|
// TODO: Use placement new? Why doesn't this work?
|
||||||
@ -4892,6 +4912,78 @@ DQN_API void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator alloca
|
|||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
// [$LLOG] Dqn_Log | | Library logging
|
// [$LLOG] Dqn_Log | | Library logging
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
|
||||||
|
bool colour,
|
||||||
|
Dqn_String8 type,
|
||||||
|
int log_type,
|
||||||
|
Dqn_CallSite call_site,
|
||||||
|
char const *fmt,
|
||||||
|
va_list args)
|
||||||
|
{
|
||||||
|
Dqn_usize header_size_no_ansi_codes = 0;
|
||||||
|
Dqn_String8 header = {};
|
||||||
|
{
|
||||||
|
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
|
||||||
|
max_type_length = DQN_MAX(max_type_length, type.size);
|
||||||
|
int type_padding = DQN_CAST(int)(max_type_length - type.size);
|
||||||
|
|
||||||
|
Dqn_String8 colour_esc = {};
|
||||||
|
Dqn_String8 bold_esc = {};
|
||||||
|
Dqn_String8 reset_esc = {};
|
||||||
|
if (colour) {
|
||||||
|
bold_esc = Dqn_Print_ESCBoldString;
|
||||||
|
reset_esc = Dqn_Print_ESCResetString;
|
||||||
|
switch (log_type) {
|
||||||
|
case Dqn_LogType_Debug: break;
|
||||||
|
case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break;
|
||||||
|
case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break;
|
||||||
|
case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
|
||||||
|
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
|
||||||
|
header = Dqn_String8_InitF(allocator,
|
||||||
|
"%.*s " // date
|
||||||
|
"%.*s " // hms
|
||||||
|
"%.*s" // colour
|
||||||
|
"%.*s" // bold
|
||||||
|
"%.*s" // type
|
||||||
|
"%*s" // type padding
|
||||||
|
"%.*s" // reset
|
||||||
|
" %.*s" // file name
|
||||||
|
":%05u ", // line number
|
||||||
|
time.date_size - 2, time.date + 2,
|
||||||
|
time.hms_size, time.hms,
|
||||||
|
colour_esc.size, colour_esc.data,
|
||||||
|
bold_esc.size, bold_esc.data,
|
||||||
|
type.size, type.data,
|
||||||
|
type_padding, "",
|
||||||
|
reset_esc.size, reset_esc.data,
|
||||||
|
file_name.size, file_name.data,
|
||||||
|
call_site.line);
|
||||||
|
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Header padding
|
||||||
|
// =========================================================================
|
||||||
|
Dqn_usize header_padding = 0;
|
||||||
|
{
|
||||||
|
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
|
||||||
|
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
|
||||||
|
header_padding = max_header_length - header_size_no_ansi_codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Construct final log
|
||||||
|
// =========================================================================
|
||||||
|
Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args);
|
||||||
|
Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||||
|
DQN_MEMCPY(result.data, header.data, header.size);
|
||||||
|
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||||
|
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||||
{
|
{
|
||||||
(void)log_type;
|
(void)log_type;
|
||||||
@ -4911,68 +5003,13 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use
|
|||||||
// NOTE: Generate the log header
|
// NOTE: Generate the log header
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||||
Dqn_usize header_size_no_ansi_codes = 0;
|
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
|
||||||
Dqn_String8 header = {};
|
!dqn_library.log_no_colour,
|
||||||
{
|
type,
|
||||||
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
|
log_type,
|
||||||
max_type_length = DQN_MAX(max_type_length, type.size);
|
call_site,
|
||||||
int type_padding = DQN_CAST(int)(max_type_length - type.size);
|
fmt,
|
||||||
|
args);
|
||||||
Dqn_String8 colour = {};
|
|
||||||
Dqn_String8 bold = {};
|
|
||||||
Dqn_String8 reset = {};
|
|
||||||
if (!dqn_library.log_no_colour) {
|
|
||||||
bold = Dqn_Print_ESCBoldString;
|
|
||||||
reset = Dqn_Print_ESCResetString;
|
|
||||||
switch (log_type) {
|
|
||||||
case Dqn_LogType_Debug: break;
|
|
||||||
case Dqn_LogType_Info: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break;
|
|
||||||
case Dqn_LogType_Warning: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break;
|
|
||||||
case Dqn_LogType_Error: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
|
|
||||||
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
|
|
||||||
header = Dqn_String8_InitF(scratch.allocator,
|
|
||||||
"%.*s " // date
|
|
||||||
"%.*s " // hms
|
|
||||||
"%.*s" // colour
|
|
||||||
"%.*s" // bold
|
|
||||||
"%.*s" // type
|
|
||||||
"%*s" // type padding
|
|
||||||
"%.*s" // reset
|
|
||||||
" %.*s" // file name
|
|
||||||
":%05u ", // line number
|
|
||||||
time.date_size - 2, time.date + 2,
|
|
||||||
time.hms_size, time.hms,
|
|
||||||
colour.size, colour.data,
|
|
||||||
bold.size, bold.data,
|
|
||||||
type.size, type.data,
|
|
||||||
type_padding, "",
|
|
||||||
reset.size, reset.data,
|
|
||||||
file_name.size, file_name.data,
|
|
||||||
call_site.line);
|
|
||||||
header_size_no_ansi_codes = header.size - colour.size - Dqn_Print_ESCResetString.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Header padding
|
|
||||||
// =========================================================================
|
|
||||||
Dqn_usize header_padding = 0;
|
|
||||||
{
|
|
||||||
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
|
|
||||||
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
|
|
||||||
header_padding = max_header_length - header_size_no_ansi_codes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Construct final log
|
|
||||||
// =========================================================================
|
|
||||||
Dqn_String8 msg = Dqn_String8_InitFV(scratch.allocator, fmt, args);
|
|
||||||
|
|
||||||
Dqn_String8 log_line = Dqn_String8_Allocate(scratch.allocator, header.size + header_padding + msg.size, Dqn_ZeroMem_No);
|
|
||||||
DQN_MEMCPY(log_line.data, header.data, header.size);
|
|
||||||
DQN_MEMSET(log_line.data + header.size, ' ', header_padding);
|
|
||||||
DQN_MEMCPY(log_line.data + header.size + header_padding, msg.data, msg.size);
|
|
||||||
|
|
||||||
// NOTE: Print log
|
// NOTE: Print log
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@ -6362,12 +6399,15 @@ DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
|||||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
||||||
{
|
{
|
||||||
Dqn_ArenaTempMemory result = {};
|
Dqn_ArenaTempMemory result = {};
|
||||||
|
if (arena) {
|
||||||
|
arena->temp_memory_count++;
|
||||||
result.arena = arena;
|
result.arena = arena;
|
||||||
result.head = arena->head;
|
result.head = arena->head;
|
||||||
result.curr = arena->curr;
|
result.curr = arena->curr;
|
||||||
result.tail = arena->tail;
|
result.tail = arena->tail;
|
||||||
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||||
result.stats = arena->stats;
|
result.stats = arena->stats;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6376,8 +6416,12 @@ DQN_API void Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemor
|
|||||||
if (!scope.arena)
|
if (!scope.arena)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// NOTE: Revert arena stats
|
|
||||||
Dqn_Arena *arena = scope.arena;
|
Dqn_Arena *arena = scope.arena;
|
||||||
|
if (!DQN_CHECKF(arena->temp_memory_count > 0, "End temp memory has been called without a matching begin pair on the arena"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// NOTE: Revert arena stats
|
||||||
|
arena->temp_memory_count--;
|
||||||
arena->stats.capacity = scope.stats.capacity;
|
arena->stats.capacity = scope.stats.capacity;
|
||||||
arena->stats.used = scope.stats.used;
|
arena->stats.used = scope.stats.used;
|
||||||
arena->stats.wasted = scope.stats.wasted;
|
arena->stats.wasted = scope.stats.wasted;
|
||||||
@ -6597,6 +6641,9 @@ DQN_API void Dqn_Arena_Free_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroM
|
|||||||
if (!arena)
|
if (!arena)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!DQN_CHECKF(arena->temp_memory_count == 0, "You cannot free an arena whilst in an temp memory region"))
|
||||||
|
return;
|
||||||
|
|
||||||
while (arena->tail) {
|
while (arena->tail) {
|
||||||
Dqn_ArenaBlock *block = arena->tail;
|
Dqn_ArenaBlock *block = arena->tail;
|
||||||
arena->tail = block->prev;
|
arena->tail = block->prev;
|
||||||
@ -7148,52 +7195,39 @@ DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check)
|
|||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
DQN_API bool Dqn_Safe_AssertF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...)
|
|
||||||
{
|
|
||||||
bool result = assertion_expr;
|
|
||||||
if (!result) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, call_site, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
DQN_DEBUG_BREAK;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
||||||
{
|
{
|
||||||
int64_t result = Dqn_Safe_AssertF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
int64_t result = DQN_CHECKF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b)
|
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b)
|
||||||
{
|
{
|
||||||
int64_t result = Dqn_Safe_AssertF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
int64_t result = DQN_CHECKF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b)
|
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b)
|
||||||
{
|
{
|
||||||
uint64_t result = Dqn_Safe_AssertF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
uint64_t result = DQN_CHECKF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b)
|
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b)
|
||||||
{
|
{
|
||||||
uint64_t result = Dqn_Safe_AssertF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
uint64_t result = DQN_CHECKF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b)
|
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b)
|
||||||
{
|
{
|
||||||
uint64_t result = Dqn_Safe_AssertF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
uint64_t result = DQN_CHECKF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
||||||
{
|
{
|
||||||
uint32_t result = Dqn_Safe_AssertF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
uint32_t result = DQN_CHECKF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7203,31 +7237,31 @@ DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
|||||||
// the highest possible rank (unsigned > signed).
|
// the highest possible rank (unsigned > signed).
|
||||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val)
|
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val)
|
||||||
{
|
{
|
||||||
int result = Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
int result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val)
|
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val)
|
||||||
{
|
{
|
||||||
int8_t result = Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= INT8_MAX) ? DQN_CAST(int8_t)val : INT8_MAX;
|
int8_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT8_MAX) ? DQN_CAST(int8_t)val : INT8_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val)
|
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val)
|
||||||
{
|
{
|
||||||
int16_t result = Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= INT16_MAX) ? DQN_CAST(int16_t)val : INT16_MAX;
|
int16_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT16_MAX) ? DQN_CAST(int16_t)val : INT16_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val)
|
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val)
|
||||||
{
|
{
|
||||||
int32_t result = Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= INT32_MAX) ? DQN_CAST(int32_t)val : INT32_MAX;
|
int32_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT32_MAX) ? DQN_CAST(int32_t)val : INT32_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
||||||
{
|
{
|
||||||
int64_t result = Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= INT64_MAX) ? DQN_CAST(int64_t)val : INT64_MAX;
|
int64_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT64_MAX) ? DQN_CAST(int64_t)val : INT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7237,25 +7271,25 @@ DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
|||||||
// match the highest rank operand.
|
// match the highest rank operand.
|
||||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val)
|
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val)
|
||||||
{
|
{
|
||||||
uint8_t result = Dqn_Safe_Assert(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val)
|
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val)
|
||||||
{
|
{
|
||||||
uint16_t result = Dqn_Safe_Assert(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val)
|
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val)
|
||||||
{
|
{
|
||||||
uint32_t result = Dqn_Safe_Assert(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
||||||
{
|
{
|
||||||
uint64_t result = Dqn_Safe_Assert(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX;
|
uint64_t result = DQN_CHECK(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7265,25 +7299,25 @@ DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
|||||||
// match the highest rank operand.
|
// match the highest rank operand.
|
||||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
||||||
{
|
{
|
||||||
unsigned int result = Dqn_Safe_Assert(val <= UINT8_MAX) ? DQN_CAST(unsigned int)val : UINT_MAX;
|
unsigned int result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(unsigned int)val : UINT_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val)
|
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val)
|
||||||
{
|
{
|
||||||
uint8_t result = Dqn_Safe_Assert(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val)
|
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val)
|
||||||
{
|
{
|
||||||
uint16_t result = Dqn_Safe_Assert(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
||||||
{
|
{
|
||||||
uint32_t result = Dqn_Safe_Assert(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7294,35 +7328,35 @@ DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
|||||||
// match the highest rank operand.
|
// match the highest rank operand.
|
||||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val)
|
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT_MIN && val <= INT_MAX);
|
DQN_ASSERT(val >= INT_MIN && val <= INT_MAX);
|
||||||
int result = DQN_CAST(int)DQN_CLAMP(val, INT_MIN, INT_MAX);
|
int result = DQN_CAST(int)DQN_CLAMP(val, INT_MIN, INT_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val)
|
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT8_MIN && val <= INT8_MAX);
|
DQN_ASSERT(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val)
|
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT16_MIN && val <= INT16_MAX);
|
DQN_ASSERT(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val)
|
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT32_MIN && val <= INT32_MAX);
|
DQN_ASSERT(val >= INT32_MIN && val <= INT32_MAX);
|
||||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT64_MIN && val <= INT64_MAX);
|
DQN_ASSERT(val >= INT64_MIN && val <= INT64_MAX);
|
||||||
int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX);
|
int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -7335,8 +7369,8 @@ DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
|||||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
||||||
{
|
{
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT_MAX))
|
||||||
result = DQN_CAST(unsigned int)val;
|
result = DQN_CAST(unsigned int)val;
|
||||||
else
|
else
|
||||||
result = UINT_MAX;
|
result = UINT_MAX;
|
||||||
@ -7347,8 +7381,8 @@ DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
|||||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
||||||
{
|
{
|
||||||
uint8_t result = 0;
|
uint8_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||||
result = DQN_CAST(uint8_t)val;
|
result = DQN_CAST(uint8_t)val;
|
||||||
else
|
else
|
||||||
result = UINT8_MAX;
|
result = UINT8_MAX;
|
||||||
@ -7359,8 +7393,8 @@ DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
|||||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
||||||
{
|
{
|
||||||
uint16_t result = 0;
|
uint16_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||||
result = DQN_CAST(uint16_t)val;
|
result = DQN_CAST(uint16_t)val;
|
||||||
else
|
else
|
||||||
result = UINT16_MAX;
|
result = UINT16_MAX;
|
||||||
@ -7371,8 +7405,8 @@ DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
|||||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
||||||
{
|
{
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT32_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT32_MAX))
|
||||||
result = DQN_CAST(uint32_t)val;
|
result = DQN_CAST(uint32_t)val;
|
||||||
else
|
else
|
||||||
result = UINT32_MAX;
|
result = UINT32_MAX;
|
||||||
@ -7383,8 +7417,8 @@ DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
|||||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
||||||
{
|
{
|
||||||
uint64_t result = 0;
|
uint64_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT64_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT64_MAX))
|
||||||
result = DQN_CAST(uint64_t)val;
|
result = DQN_CAST(uint64_t)val;
|
||||||
else
|
else
|
||||||
result = UINT64_MAX;
|
result = UINT64_MAX;
|
||||||
@ -7398,28 +7432,28 @@ DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
|||||||
// match the highest rank operand.
|
// match the highest rank operand.
|
||||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(int64_t val)
|
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(int64_t val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= DQN_ISIZE_MIN && val <= DQN_ISIZE_MAX);
|
DQN_CHECK(val >= DQN_ISIZE_MIN && val <= DQN_ISIZE_MAX);
|
||||||
Dqn_isize result = DQN_CAST(int64_t)DQN_CLAMP(val, DQN_ISIZE_MIN, DQN_ISIZE_MAX);
|
Dqn_isize result = DQN_CAST(int64_t)DQN_CLAMP(val, DQN_ISIZE_MIN, DQN_ISIZE_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val)
|
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT8_MIN && val <= INT8_MAX);
|
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val)
|
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT16_MIN && val <= INT16_MAX);
|
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT32_MIN && val <= INT32_MAX);
|
DQN_CHECK(val >= INT32_MIN && val <= INT32_MAX);
|
||||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -7428,14 +7462,14 @@ DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val)
|
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT8_MIN && val <= INT8_MAX);
|
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
||||||
{
|
{
|
||||||
Dqn_Safe_Assert(val >= INT16_MIN && val <= INT16_MAX);
|
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -7443,8 +7477,8 @@ DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
|||||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
||||||
{
|
{
|
||||||
uint8_t result = 0;
|
uint8_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||||
result = DQN_CAST(uint8_t)val;
|
result = DQN_CAST(uint8_t)val;
|
||||||
else
|
else
|
||||||
result = UINT8_MAX;
|
result = UINT8_MAX;
|
||||||
@ -7455,8 +7489,8 @@ DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
|||||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val)
|
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val)
|
||||||
{
|
{
|
||||||
uint16_t result = 0;
|
uint16_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= DQN_CAST(Dqn_isize)0)) {
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
if (Dqn_Safe_Assert(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||||
result = DQN_CAST(uint16_t)val;
|
result = DQN_CAST(uint16_t)val;
|
||||||
else
|
else
|
||||||
result = UINT16_MAX;
|
result = UINT16_MAX;
|
||||||
@ -7468,7 +7502,7 @@ DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val)
|
|||||||
{
|
{
|
||||||
static_assert(sizeof(val) <= sizeof(uint32_t), "Sanity check to allow simplifying of casting");
|
static_assert(sizeof(val) <= sizeof(uint32_t), "Sanity check to allow simplifying of casting");
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= 0))
|
if (DQN_CHECK(val >= 0))
|
||||||
result = DQN_CAST(uint32_t)val;
|
result = DQN_CAST(uint32_t)val;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -7477,7 +7511,7 @@ DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val)
|
|||||||
{
|
{
|
||||||
static_assert(sizeof(val) <= sizeof(uint64_t), "Sanity check to allow simplifying of casting");
|
static_assert(sizeof(val) <= sizeof(uint64_t), "Sanity check to allow simplifying of casting");
|
||||||
uint64_t result = 0;
|
uint64_t result = 0;
|
||||||
if (Dqn_Safe_Assert(val >= 0))
|
if (DQN_CHECK(val >= 0))
|
||||||
result = DQN_CAST(uint64_t)val;
|
result = DQN_CAST(uint64_t)val;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -7727,7 +7761,7 @@ DQN_API bool Dqn_Bin_BytesToHexBuffer(void const *src, Dqn_usize src_size, char
|
|||||||
if (!src || !dest)
|
if (!src || !dest)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Dqn_Safe_Assert(dest_size >= src_size * 2))
|
if (!DQN_CHECK(dest_size >= src_size * 2))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char const *HEX = "0123456789abcdef";
|
char const *HEX = "0123456789abcdef";
|
||||||
@ -8433,7 +8467,7 @@ DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle,
|
|||||||
if (handle->state < Dqn_WinNetHandleState_HttpMethodReady)
|
if (handle->state < Dqn_WinNetHandleState_HttpMethodReady)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Dqn_Safe_Assert(handle->http_handle))
|
if (!DQN_CHECK(handle->http_handle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
unsigned long modifier = 0;
|
unsigned long modifier = 0;
|
||||||
@ -8489,7 +8523,7 @@ DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle *
|
|||||||
handle->state = Dqn_WinNetHandleState_RequestGood;
|
handle->state = Dqn_WinNetHandleState_RequestGood;
|
||||||
unsigned long buffer_size = 0;
|
unsigned long buffer_size = 0;
|
||||||
int query_result = HttpQueryInfoA(handle->http_handle, HTTP_QUERY_RAW_HEADERS_CRLF, nullptr, &buffer_size, nullptr);
|
int query_result = HttpQueryInfoA(handle->http_handle, HTTP_QUERY_RAW_HEADERS_CRLF, nullptr, &buffer_size, nullptr);
|
||||||
if (!Dqn_Safe_Assert(query_result != ERROR_INSUFFICIENT_BUFFER))
|
if (!DQN_CHECK(query_result != ERROR_INSUFFICIENT_BUFFER))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result.raw_headers = Dqn_String8_Allocate(allocator, buffer_size, Dqn_ZeroMem_No);
|
result.raw_headers = Dqn_String8_Allocate(allocator, buffer_size, Dqn_ZeroMem_No);
|
||||||
@ -9326,7 +9360,7 @@ DQN_API char *Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE_FUNCTION char const *path, Dqn
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned long const bytes_desired = DQN_CAST(unsigned long)win_file_size.QuadPart;
|
unsigned long const bytes_desired = DQN_CAST(unsigned long)win_file_size.QuadPart;
|
||||||
if (!Dqn_Safe_AssertF(bytes_desired == win_file_size.QuadPart,
|
if (!DQN_CHECKF(bytes_desired == win_file_size.QuadPart,
|
||||||
"Current implementation doesn't support >4GiB, implement Win32 overlapped IO")) {
|
"Current implementation doesn't support >4GiB, implement Win32 overlapped IO")) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
256
dqn_tester.h
256
dqn_tester.h
@ -1,256 +0,0 @@
|
|||||||
#if !defined(DQN_TESTER_H)
|
|
||||||
#define DQN_TESTER_H
|
|
||||||
//
|
|
||||||
// NOTE: Overview
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// A super minimal testing framework, most of the logic here is the pretty
|
|
||||||
// printing of test results.
|
|
||||||
|
|
||||||
// NOTE: Configuration
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// #define DQN_TESTER_IMPLEMENTATION
|
|
||||||
// Define this in one and only one C++ file to enable the implementation
|
|
||||||
// code of the header file. This will also automatically enable the JSMN
|
|
||||||
// implementation.
|
|
||||||
//
|
|
||||||
// #define DQN_TESTER_RESULT_LPAD
|
|
||||||
// Define this to a number to specify how much to pad the output of the test
|
|
||||||
// result line before the test result is printed.
|
|
||||||
//
|
|
||||||
// #define DQN_TESTER_RESULT_PAD_CHAR
|
|
||||||
// Define this to a character to specify the default character to use for
|
|
||||||
// padding. By default this is '.'
|
|
||||||
//
|
|
||||||
// #define DQN_TESTER_SPACING
|
|
||||||
// Define this to a number to specify the number of spaces between the group
|
|
||||||
// declaration and the test output in the group.
|
|
||||||
//
|
|
||||||
// #define DQN_TESTER_BAD_COLOR
|
|
||||||
// Define this to a terminal color code to specify what color errors will be
|
|
||||||
// presented as.
|
|
||||||
//
|
|
||||||
// #define DQN_TESTER_GOOD_COLOR
|
|
||||||
// Define this to a terminal color code to specify what color sucess will be
|
|
||||||
// presented as.
|
|
||||||
|
|
||||||
// NOTE: Macros
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_RESULT_LPAD)
|
|
||||||
#define DQN_TESTER_RESULT_LPAD 90
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_RESULT_PAD_CHAR)
|
|
||||||
#define DQN_TESTER_RESULT_PAD_CHAR '.'
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_SPACING)
|
|
||||||
#define DQN_TESTER_SPACING 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_BAD_COLOR)
|
|
||||||
#define DQN_TESTER_BAD_COLOR "\x1b[31m"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_GOOD_COLOR)
|
|
||||||
#define DQN_TESTER_GOOD_COLOR "\x1b[32m"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DQN_TESTER_COLOR_RESET "\x1b[0m"
|
|
||||||
#define DQN_TESTER_TOKEN_COMBINE2(x, y) x ## y_
|
|
||||||
#define DQN_TESTER_TOKEN_COMBINE(x, y) DQN_TESTER_TOKEN_COMBINE2(x, y)
|
|
||||||
|
|
||||||
/// Creates <prefix><line_number>_ .e. group_123_
|
|
||||||
#define DQN_TESTER_UNIQUE_NAME(prefix) \
|
|
||||||
DQN_TESTER_TOKEN_COMBINE(DQN_TESTER_TOKEN_COMBINE(prefix, __LINE__), _)
|
|
||||||
|
|
||||||
#define DQN_TESTER_GROUP(group, fmt, ...) \
|
|
||||||
for (Dqn_Tester *test_group_ = (group = Dqn_Tester_BeginGroup(fmt, ## __VA_ARGS__), &group); \
|
|
||||||
!test_group_->finished; \
|
|
||||||
Dqn_Tester_EndGroup(test_group_))
|
|
||||||
|
|
||||||
#define DQN_TESTER_TEST(fmt, ...) \
|
|
||||||
for (int DQN_TESTER_UNIQUE_NAME(dummy_) = (Dqn_Tester_Begin(test_group_, fmt, ## __VA_ARGS__), 0); \
|
|
||||||
(void)DQN_TESTER_UNIQUE_NAME(dummy_), test_group_->state == Dqn_TesterTestState_TestBegun; \
|
|
||||||
Dqn_Tester_End(test_group_))
|
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERTF(test, expr, fmt, ...) \
|
|
||||||
DQN_TESTER_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERT(test, expr) DQN_TESTER_ASSERT_AT((test), __FILE__, __LINE__, (expr))
|
|
||||||
|
|
||||||
#define DQN_TESTER_LOG(test, fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if ((test)->log_count++ == 0) { \
|
|
||||||
fprintf(stdout, "\n"); \
|
|
||||||
} \
|
|
||||||
fprintf(stdout, "%*sLog: " fmt "\n", DQN_TESTER_SPACING * 2, "", ##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERTF_AT(test, file, line, expr, fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (!(expr)) { \
|
|
||||||
if ((test)->log_count++ == 0) { \
|
|
||||||
fprintf(stdout, "\n"); \
|
|
||||||
} \
|
|
||||||
(test)->state = Dqn_TesterTestState_TestFailed; \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"%*sAssertion Triggered\n" \
|
|
||||||
"%*sFile: %s:%d\n" \
|
|
||||||
"%*sExpression: [" #expr "]\n" \
|
|
||||||
"%*sReason: " fmt "\n", \
|
|
||||||
DQN_TESTER_SPACING * 2, \
|
|
||||||
"", \
|
|
||||||
DQN_TESTER_SPACING * 3, \
|
|
||||||
"", \
|
|
||||||
file, \
|
|
||||||
line, \
|
|
||||||
DQN_TESTER_SPACING * 3, \
|
|
||||||
"", \
|
|
||||||
DQN_TESTER_SPACING * 3, \
|
|
||||||
"", \
|
|
||||||
##__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERT_AT(test, file, line, expr) \
|
|
||||||
do { \
|
|
||||||
if (!(expr)) { \
|
|
||||||
if ((test)->log_count++ == 0) { \
|
|
||||||
fprintf(stdout, "\n"); \
|
|
||||||
} \
|
|
||||||
(test)->state = Dqn_TesterTestState_TestFailed; \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"%*sFile: %s:%d\n" \
|
|
||||||
"%*sExpression: [" #expr "]\n", \
|
|
||||||
DQN_TESTER_SPACING * 2, \
|
|
||||||
"", \
|
|
||||||
file, \
|
|
||||||
line, \
|
|
||||||
DQN_TESTER_SPACING * 2, \
|
|
||||||
""); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// NOTE: Header
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
typedef enum Dqn_TesterTestState {
|
|
||||||
Dqn_TesterTestState_Nil,
|
|
||||||
Dqn_TesterTestState_TestBegun,
|
|
||||||
Dqn_TesterTestState_TestFailed,
|
|
||||||
} Dqn_TesterTestState;
|
|
||||||
|
|
||||||
typedef struct Dqn_Tester {
|
|
||||||
int num_tests_in_group;
|
|
||||||
int num_tests_ok_in_group;
|
|
||||||
int log_count;
|
|
||||||
Dqn_TesterTestState state;
|
|
||||||
bool finished;
|
|
||||||
} Dqn_Tester;
|
|
||||||
|
|
||||||
Dqn_Tester Dqn_Tester_BeginGroupV(char const *fmt, va_list args);
|
|
||||||
Dqn_Tester Dqn_Tester_BeginGroup(char const *fmt, ...);
|
|
||||||
void Dqn_Tester_EndGroup(Dqn_Tester *test);
|
|
||||||
|
|
||||||
void Dqn_Tester_BeginV(Dqn_Tester *test, char const *fmt, va_list args);
|
|
||||||
void Dqn_Tester_Begin(Dqn_Tester *test, char const *fmt, ...);
|
|
||||||
void Dqn_Tester_End(Dqn_Tester *test);
|
|
||||||
|
|
||||||
// NOTE: Implementation
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
#if defined(DQN_TESTER_IMPLEMENTATION)
|
|
||||||
Dqn_Tester Dqn_Tester_BeginGroupV(char const *fmt, va_list args)
|
|
||||||
{
|
|
||||||
fprintf(stdout, fmt, args);
|
|
||||||
fputc('\n', stdout);
|
|
||||||
Dqn_Tester result = {};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dqn_Tester Dqn_Tester_BeginGroup(char const *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Dqn_Tester result = Dqn_Tester_BeginGroupV(fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Tester_EndGroup(Dqn_Tester *test)
|
|
||||||
{
|
|
||||||
if (test->finished)
|
|
||||||
return;
|
|
||||||
|
|
||||||
test->finished = true;
|
|
||||||
bool all_clear = test->num_tests_ok_in_group == test->num_tests_in_group;
|
|
||||||
fprintf(stdout,
|
|
||||||
"%s\n %02d/%02d tests passed -- %s\n\n" DQN_TESTER_COLOR_RESET,
|
|
||||||
all_clear ? DQN_TESTER_GOOD_COLOR : DQN_TESTER_BAD_COLOR,
|
|
||||||
test->num_tests_ok_in_group,
|
|
||||||
test->num_tests_in_group,
|
|
||||||
all_clear ? "OK" : "FAILED");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Tester_BeginV(Dqn_Tester *test, char const *fmt, va_list args)
|
|
||||||
{
|
|
||||||
assert(test->state == Dqn_TesterTestState_Nil &&
|
|
||||||
"Nesting a unit test within another unit test is not allowed, ensure"
|
|
||||||
"the first test has finished by calling Dqn_Tester_End");
|
|
||||||
|
|
||||||
test->num_tests_in_group++;
|
|
||||||
test->state = Dqn_TesterTestState_TestBegun;
|
|
||||||
test->log_count = 0;
|
|
||||||
|
|
||||||
int size_required = 0;
|
|
||||||
{
|
|
||||||
va_list args_copy;
|
|
||||||
va_copy(args_copy, args);
|
|
||||||
size_required = vsnprintf(NULL, 0, fmt, args_copy);
|
|
||||||
va_end(args_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%*s", DQN_TESTER_SPACING, "");
|
|
||||||
vprintf(fmt, args);
|
|
||||||
for (int pad = DQN_TESTER_SPACING + size_required; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
|
||||||
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Tester_Begin(Dqn_Tester *test, char const *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Dqn_Tester_BeginV(test, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Tester_End(Dqn_Tester *test)
|
|
||||||
{
|
|
||||||
assert(test->state != Dqn_TesterTestState_Nil && "Test was marked as ended but a test was never commenced using Dqn_Tester_Begin");
|
|
||||||
if (test->log_count != 0) {
|
|
||||||
// NOTE: We try and print the result on the same line as the test name,
|
|
||||||
// but if there were logs printed throughout the test then we must print
|
|
||||||
// the result on a new line.
|
|
||||||
printf("%*s", DQN_TESTER_SPACING, "");
|
|
||||||
for (int pad = DQN_TESTER_SPACING; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
|
||||||
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test->state == Dqn_TesterTestState_TestFailed) {
|
|
||||||
fprintf(stdout, DQN_TESTER_BAD_COLOR " FAILED");
|
|
||||||
} else {
|
|
||||||
fprintf(stdout, DQN_TESTER_GOOD_COLOR " OK");
|
|
||||||
test->num_tests_ok_in_group++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stdout, DQN_TESTER_COLOR_RESET "\n");
|
|
||||||
if (test->log_count != 0) {
|
|
||||||
putc('\n', stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
test->state = Dqn_TesterTestState_Nil;
|
|
||||||
}
|
|
||||||
#endif // DQN_TESTER_IMPLEMENTATION
|
|
||||||
#endif // DQN_TESTER_H
|
|
1120
dqn_unit_tests.cpp
1120
dqn_unit_tests.cpp
File diff suppressed because it is too large
Load Diff
215
dqn_utest.h
Normal file
215
dqn_utest.h
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
#if !defined(DQN_UTEST_H)
|
||||||
|
#define DQN_UTEST_H
|
||||||
|
//
|
||||||
|
// NOTE: Overview
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// A super minimal testing framework, most of the logic here is the pretty
|
||||||
|
// printing of test results.
|
||||||
|
|
||||||
|
// NOTE: Configuration
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// #define DQN_UTEST_IMPLEMENTATION
|
||||||
|
// Define this in one and only one C++ file to enable the implementation
|
||||||
|
// code of the header file. This will also automatically enable the JSMN
|
||||||
|
// implementation.
|
||||||
|
//
|
||||||
|
// #define DQN_UTEST_RESULT_LPAD
|
||||||
|
// Define this to a number to specify how much to pad the output of the test
|
||||||
|
// result line before the test result is printed.
|
||||||
|
//
|
||||||
|
// #define DQN_UTEST_RESULT_PAD_CHAR
|
||||||
|
// Define this to a character to specify the default character to use for
|
||||||
|
// padding. By default this is '.'
|
||||||
|
//
|
||||||
|
// #define DQN_UTEST_SPACING
|
||||||
|
// Define this to a number to specify the number of spaces between the group
|
||||||
|
// declaration and the test output in the group.
|
||||||
|
//
|
||||||
|
// #define DQN_UTEST_BAD_COLOR
|
||||||
|
// Define this to a terminal color code to specify what color errors will be
|
||||||
|
// presented as.
|
||||||
|
//
|
||||||
|
// #define DQN_UTEST_GOOD_COLOR
|
||||||
|
// Define this to a terminal color code to specify what color sucess will be
|
||||||
|
// presented as.
|
||||||
|
|
||||||
|
// NOTE: Macros
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if !defined(DQN_UTEST_RESULT_LPAD)
|
||||||
|
#define DQN_UTEST_RESULT_LPAD 90
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_UTEST_RESULT_PAD_CHAR)
|
||||||
|
#define DQN_UTEST_RESULT_PAD_CHAR '.'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_UTEST_SPACING)
|
||||||
|
#define DQN_UTEST_SPACING 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_UTEST_BAD_COLOR)
|
||||||
|
#define DQN_UTEST_BAD_COLOR "\x1b[31m"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_UTEST_GOOD_COLOR)
|
||||||
|
#define DQN_UTEST_GOOD_COLOR "\x1b[32m"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DQN_UTEST_COLOR_RESET "\x1b[0m"
|
||||||
|
|
||||||
|
#define DQN_UTEST_GROUP(test, fmt, ...) \
|
||||||
|
for (Dqn_UTest *test_var_ = (printf(fmt "\n", ## __VA_ARGS__), &test); \
|
||||||
|
test_var_ != nullptr; \
|
||||||
|
Dqn_UTest_PrintStats(&test), test_var_ = nullptr)
|
||||||
|
|
||||||
|
#define DQN_UTEST_TEST(fmt, ...) \
|
||||||
|
for (int dummy_ = (Dqn_UTest_Begin(test_var_, fmt, ## __VA_ARGS__), 0); \
|
||||||
|
(void)dummy_, test_var_->state == Dqn_UTestState_TestBegun; \
|
||||||
|
Dqn_UTest_End(test_var_))
|
||||||
|
|
||||||
|
#define DQN_UTEST_ASSERTF(test, expr, fmt, ...) \
|
||||||
|
DQN_UTEST_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define DQN_UTEST_ASSERT(test, expr) \
|
||||||
|
DQN_UTEST_ASSERT_AT((test), __FILE__, __LINE__, (expr))
|
||||||
|
|
||||||
|
// TODO: Fix the logs. They print before the tests, we should accumulate logs
|
||||||
|
// per test, then, dump them on test on. But to do this nicely without crappy
|
||||||
|
// mem management we need to implement an arena.
|
||||||
|
#define DQN_UTEST_LOG(fmt, ...) \
|
||||||
|
fprintf(stdout, "%*s" fmt "\n", DQN_UTEST_SPACING * 2, "", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define DQN_UTEST_ASSERTF_AT(test, file, line, expr, fmt, ...) \
|
||||||
|
do { \
|
||||||
|
if (!(expr)) { \
|
||||||
|
(test)->state = Dqn_UTestState_TestFailed; \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"%*sAssertion Triggered\n" \
|
||||||
|
"%*sFile: %s:%d\n" \
|
||||||
|
"%*sExpression: [" #expr "]\n" \
|
||||||
|
"%*sReason: " fmt "\n", \
|
||||||
|
DQN_UTEST_SPACING * 2, \
|
||||||
|
"", \
|
||||||
|
DQN_UTEST_SPACING * 3, \
|
||||||
|
"", \
|
||||||
|
file, \
|
||||||
|
line, \
|
||||||
|
DQN_UTEST_SPACING * 3, \
|
||||||
|
"", \
|
||||||
|
DQN_UTEST_SPACING * 3, \
|
||||||
|
"", \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DQN_UTEST_ASSERT_AT(test, file, line, expr) \
|
||||||
|
do { \
|
||||||
|
if (!(expr)) { \
|
||||||
|
(test)->state = Dqn_UTestState_TestFailed; \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"%*sFile: %s:%d\n" \
|
||||||
|
"%*sExpression: [" #expr "]\n", \
|
||||||
|
DQN_UTEST_SPACING * 2, \
|
||||||
|
"", \
|
||||||
|
file, \
|
||||||
|
line, \
|
||||||
|
DQN_UTEST_SPACING * 2, \
|
||||||
|
""); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// NOTE: Header
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
typedef enum Dqn_UTestState {
|
||||||
|
Dqn_UTestState_Nil,
|
||||||
|
Dqn_UTestState_TestBegun,
|
||||||
|
Dqn_UTestState_TestFailed,
|
||||||
|
} Dqn_UTestState;
|
||||||
|
|
||||||
|
typedef struct Dqn_UTest {
|
||||||
|
int num_tests_in_group;
|
||||||
|
int num_tests_ok_in_group;
|
||||||
|
Dqn_UTestState state;
|
||||||
|
bool finished;
|
||||||
|
char name[1024];
|
||||||
|
size_t name_size;
|
||||||
|
} Dqn_UTest;
|
||||||
|
|
||||||
|
void Dqn_UTest_PrintStats(Dqn_UTest *test);
|
||||||
|
void Dqn_UTest_BeginV(Dqn_UTest *test, char const *fmt, va_list args);
|
||||||
|
void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...);
|
||||||
|
void Dqn_UTest_End(Dqn_UTest *test);
|
||||||
|
|
||||||
|
// NOTE: Implementation
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#if defined(DQN_UTEST_IMPLEMENTATION)
|
||||||
|
void Dqn_UTest_PrintStats(Dqn_UTest *test)
|
||||||
|
{
|
||||||
|
if (test->finished)
|
||||||
|
return;
|
||||||
|
|
||||||
|
test->finished = true;
|
||||||
|
bool all_clear = test->num_tests_ok_in_group == test->num_tests_in_group;
|
||||||
|
fprintf(stdout,
|
||||||
|
"%s\n %02d/%02d tests passed -- %s\n\n" DQN_UTEST_COLOR_RESET,
|
||||||
|
all_clear ? DQN_UTEST_GOOD_COLOR : DQN_UTEST_BAD_COLOR,
|
||||||
|
test->num_tests_ok_in_group,
|
||||||
|
test->num_tests_in_group,
|
||||||
|
all_clear ? "OK" : "FAILED");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dqn_UTest_BeginV(Dqn_UTest *test, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
assert(test->state == Dqn_UTestState_Nil &&
|
||||||
|
"Nesting a unit test within another unit test is not allowed, ensure"
|
||||||
|
"the first test has finished by calling Dqn_UTest_End");
|
||||||
|
|
||||||
|
test->num_tests_in_group++;
|
||||||
|
test->state = Dqn_UTestState_TestBegun;
|
||||||
|
|
||||||
|
test->name_size = 0;
|
||||||
|
{
|
||||||
|
va_list args_copy;
|
||||||
|
va_copy(args_copy, args);
|
||||||
|
test->name_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||||
|
va_end(args_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(test->name_size < sizeof(test->name));
|
||||||
|
vsnprintf(test->name, sizeof(test->name), fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_UTest_BeginV(test, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dqn_UTest_End(Dqn_UTest *test)
|
||||||
|
{
|
||||||
|
assert(test->state != Dqn_UTestState_Nil && "Test was marked as ended but a test was never commenced using Dqn_UTest_Begin");
|
||||||
|
int pad_size = DQN_UTEST_RESULT_LPAD - (DQN_UTEST_SPACING + test->name_size);
|
||||||
|
if (pad_size < 0)
|
||||||
|
pad_size = 0;
|
||||||
|
|
||||||
|
char pad_buffer[DQN_UTEST_RESULT_LPAD] = {};
|
||||||
|
memset(pad_buffer, DQN_UTEST_RESULT_PAD_CHAR, pad_size);
|
||||||
|
|
||||||
|
printf("%*s%.*s%.*s", DQN_UTEST_SPACING, "", (int)test->name_size, test->name, pad_size, pad_buffer);
|
||||||
|
if (test->state == Dqn_UTestState_TestFailed) {
|
||||||
|
printf(DQN_UTEST_BAD_COLOR " FAILED");
|
||||||
|
} else {
|
||||||
|
printf(DQN_UTEST_GOOD_COLOR " OK");
|
||||||
|
test->num_tests_ok_in_group++;
|
||||||
|
}
|
||||||
|
printf(DQN_UTEST_COLOR_RESET "\n");
|
||||||
|
test->state = Dqn_UTestState_Nil;
|
||||||
|
}
|
||||||
|
#endif // DQN_UTEST_IMPLEMENTATION
|
||||||
|
#endif // DQN_UTEST_H
|
Loading…
Reference in New Issue
Block a user