dqn: Organise into files and use an amalgamated build
This commit is contained in:
parent
807c65a253
commit
8ae369db0d
@ -26,7 +26,7 @@ pushd Build
|
|||||||
echo [ERROR] cl is not found, please put MSVC's cl on the path
|
echo [ERROR] cl is not found, please put MSVC's cl on the path
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
cl %compile_flags% %msvc_flags% %code_dir%dqn_unit_tests.cpp /Fe:dqn_unit_tests_msvc %link_flags%
|
cl %compile_flags% %msvc_flags% %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_msvc %link_flags%
|
||||||
|
|
||||||
REM Compiler: clang-cl
|
REM Compiler: clang-cl
|
||||||
REM ------------------------------------------------------------------------
|
REM ------------------------------------------------------------------------
|
||||||
@ -34,5 +34,5 @@ pushd Build
|
|||||||
echo [WARN] Optional clang compile via clang-cl if it's in the path, please put clang-cl on the path for this feature
|
echo [WARN] Optional clang compile via clang-cl if it's in the path, please put clang-cl on the path for this feature
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
clang-cl -D DQN_TEST_WITH_MAIN %code_dir%dqn_unit_tests.cpp /Fe:dqn_unit_tests_clang -link
|
clang-cl -D DQN_TEST_WITH_MAIN %code_dir%\Misc\dqn_unit_tests.cpp -I %code_dir% /Fe:dqn_unit_tests_clang -link
|
||||||
popd
|
popd
|
||||||
|
36
dqn_containers.cpp
Normal file
36
dqn_containers.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#if !defined(DQN_NO_DSMAP)
|
||||||
|
// =================================================================================================
|
||||||
|
// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
|
||||||
|
{
|
||||||
|
Dqn_DSMapKey result = {};
|
||||||
|
result.type = Dqn_DSMapKeyType_U64NoHash;
|
||||||
|
result.payload.u64 = u64;
|
||||||
|
result.hash = DQN_CAST(uint32_t)u64;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_DSMap_KeyEquals(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
|
||||||
|
switch (lhs.type) {
|
||||||
|
case Dqn_DSMapKeyType_Invalid: result = true; break;
|
||||||
|
case Dqn_DSMapKeyType_U64NoHash: result = true; break;
|
||||||
|
case Dqn_DSMapKeyType_U64: result = lhs.payload.u64 == rhs.payload.u64; break;
|
||||||
|
case Dqn_DSMapKeyType_Buffer: result = lhs.payload.buffer.size == rhs.payload.buffer.size &&
|
||||||
|
memcmp(lhs.payload.buffer.data, rhs.payload.buffer.data, lhs.payload.buffer.size) == 0; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool operator==(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs)
|
||||||
|
{
|
||||||
|
bool result = Dqn_DSMap_KeyEquals(lhs, rhs);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(DQN_NO_DSMAP)
|
||||||
|
|
1073
dqn_containers.h
Normal file
1073
dqn_containers.h
Normal file
File diff suppressed because it is too large
Load Diff
66
dqn_core.cpp
Normal file
66
dqn_core.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
|
||||||
|
// =================================================================================================
|
||||||
|
#if !defined(DQN_OS_ARM64)
|
||||||
|
#if defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||||
|
#include <cpuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||||
|
{
|
||||||
|
Dqn_CPUIDRegisters result = {};
|
||||||
|
#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.array[0] /*eax*/, &result.array[1] /*ebx*/, &result.array[2] /*ecx*/ , &result.array[3] /*edx*/);
|
||||||
|
#else
|
||||||
|
#error "Compiler not supported"
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif // !defined(DQN_OS_ARM64)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex)
|
||||||
|
{
|
||||||
|
unsigned int ticket = Dqn_Atomic_AddU32(&mutex->ticket, 1);
|
||||||
|
Dqn_TicketMutex_BeginTicket(mutex, ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_TicketMutex_End(Dqn_TicketMutex *mutex)
|
||||||
|
{
|
||||||
|
Dqn_Atomic_AddU32(&mutex->serving, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_uint Dqn_TicketMutex_MakeTicket(Dqn_TicketMutex *mutex)
|
||||||
|
{
|
||||||
|
Dqn_uint result = Dqn_Atomic_AddU32(&mutex->ticket, 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket)
|
||||||
|
{
|
||||||
|
DQN_ASSERTF(mutex->serving <= ticket,
|
||||||
|
"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_API bool Dqn_TicketMutex_CanLock(Dqn_TicketMutex const *mutex, Dqn_uint ticket)
|
||||||
|
{
|
||||||
|
bool result = (ticket == mutex->serving);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
#endif
|
488
dqn_core.h
Normal file
488
dqn_core.h
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$CFGM] Config macros | | Compile time customisation of library
|
||||||
|
// =================================================================================================
|
||||||
|
// #define DQN_IMPLEMENTATION
|
||||||
|
// Define this in one and only one C++ file to enable the implementation
|
||||||
|
// code of the header file
|
||||||
|
//
|
||||||
|
// #define DQN_NO_ASSERT
|
||||||
|
// 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 this to stop this library from defining a minimal subset of Win32
|
||||||
|
// prototypes and definitions in this file. Useful for stopping redefinition
|
||||||
|
// of symbols if another library includes "Windows.h"
|
||||||
|
//
|
||||||
|
// #define DQN_STATIC_API
|
||||||
|
// Apply static to all function definitions and disable external linkage to
|
||||||
|
// other translation units.
|
||||||
|
//
|
||||||
|
// #define DQN_STB_SPRINTF_HEADER_ONLY
|
||||||
|
// Define this to stop this library from defining
|
||||||
|
// STB_SPRINTF_IMPLEMENTATION. Useful if another library uses and includes
|
||||||
|
// "stb_sprintf.h"
|
||||||
|
//
|
||||||
|
// #define DQN_MEMSET_BYTE 0
|
||||||
|
// Change the byte that DQN_MEMSET will clear memory with. By default this
|
||||||
|
// is set to 0. Some of this library API accepts are clear memory parameter
|
||||||
|
// to scrub memory after certain operations.
|
||||||
|
//
|
||||||
|
// #define DQN_LEAK_TRACING
|
||||||
|
// When defined to some allocating calls in the library will automatically
|
||||||
|
// get passed in the file name, function name, line number and an optional
|
||||||
|
// leak_msg.
|
||||||
|
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
#error Leak tracing not supported because we enter an infinite leak tracing loop tracing our own allocations made to tracks leaks in the internal leak table.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// #define DQN_DEBUG_THREAD_CONTEXT
|
||||||
|
// Define this macro to record allocation stats for arenas used in the
|
||||||
|
// thread context. The thread context arena stats can be printed by using
|
||||||
|
// Dqn_Library_DumpThreadContextArenaStat.
|
||||||
|
//
|
||||||
|
// =================================================================================================
|
||||||
|
// [$CMAC] Compiler macros | | Macros for the compiler
|
||||||
|
// =================================================================================================
|
||||||
|
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define DQN_COMPILER_W32_CLANG
|
||||||
|
#else
|
||||||
|
#define DQN_COMPILER_W32_MSVC
|
||||||
|
#endif
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#define DQN_COMPILER_CLANG
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define DQN_COMPILER_GCC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define DQN_OS_WIN32
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
#define DQN_OS_ARM64
|
||||||
|
#else
|
||||||
|
#define DQN_OS_UNIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$MACR] Macros | | Define macros used in the library
|
||||||
|
// =================================================================================================
|
||||||
|
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++)
|
||||||
|
#define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)
|
||||||
|
|
||||||
|
#define Dqn_PowerOfTwoAlign(value, power_of_two) (((value) + ((power_of_two) - 1)) & ~((power_of_two) - 1))
|
||||||
|
#define Dqn_IsPowerOfTwo(value) (((value) & (value - 1)) == 0)
|
||||||
|
#define Dqn_IsPowerOfTwoAligned(value, power_of_two) (((value) & (power_of_two - 1)) == 0)
|
||||||
|
|
||||||
|
// NOTE: Alloc Macros ==============================================================================
|
||||||
|
#if !defined(DQN_ALLOC)
|
||||||
|
#define DQN_ALLOC(size) Dqn_VMem_Reserve(size, Dqn_VMemCommit_Yes, Dqn_VMemPage_ReadWrite)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_DEALLOC)
|
||||||
|
#define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: String.h Dependnecies =====================================================================
|
||||||
|
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
|
||||||
|
#include <string.h>
|
||||||
|
#if !defined(DQN_MEMCPY)
|
||||||
|
#define DQN_MEMCPY(dest, src, count) memcpy(dest, src, count)
|
||||||
|
#endif
|
||||||
|
#if !defined(DQN_MEMSET)
|
||||||
|
#define DQN_MEMSET(dest, value, count) memset(dest, value, count)
|
||||||
|
#endif
|
||||||
|
#if !defined(DQN_MEMCMP)
|
||||||
|
#define DQN_MEMCMP(ptr1, ptr2, num) memcmp(ptr1, ptr2, num)
|
||||||
|
#endif
|
||||||
|
#if !defined(DQN_MEMMOVE)
|
||||||
|
#define DQN_MEMMOVE(dest, src, num) memmove(dest, src, num)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: Math.h Dependnecies =======================================================================
|
||||||
|
#if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF)
|
||||||
|
#include <math.h>
|
||||||
|
#define DQN_SQRTF(val) sqrtf(val)
|
||||||
|
#if !defined(DQN_SINF)
|
||||||
|
#define DQN_SINF(val) sinf(val)
|
||||||
|
#endif
|
||||||
|
#if !defined(DQN_COSF)
|
||||||
|
#define DQN_COSF(val) cosf(val)
|
||||||
|
#endif
|
||||||
|
#if !defined(DQN_TANF)
|
||||||
|
#define DQN_TANF(val) tanf(val)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: Math Macros ===============================================================================
|
||||||
|
#define DQN_PI 3.14159265359f
|
||||||
|
|
||||||
|
#define DQN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_PI / 180.0f))
|
||||||
|
#define DQN_RADIAN_TO_DEGREE(radians) ((radians) * (180.f * DQN_PI))
|
||||||
|
|
||||||
|
#define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val))
|
||||||
|
#define DQN_MAX(a, b) ((a > b) ? (a) : (b))
|
||||||
|
#define DQN_MIN(a, b) ((a < b) ? (a) : (b))
|
||||||
|
#define DQN_CLAMP(val, lo, hi) DQN_MAX(DQN_MIN(val, hi), lo)
|
||||||
|
#define DQN_SQUARED(val) ((val) * (val))
|
||||||
|
|
||||||
|
// NOTE: Function/Variable Annotations =============================================================
|
||||||
|
#if defined(DQN_STATIC_API)
|
||||||
|
#define DQN_API static
|
||||||
|
#else
|
||||||
|
#define DQN_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DQN_LOCAL_PERSIST static
|
||||||
|
#define DQN_FILE_SCOPE static
|
||||||
|
#define DQN_CAST(val) (val)
|
||||||
|
|
||||||
|
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
#define DQN_FORCE_INLINE __forceinline
|
||||||
|
#else
|
||||||
|
#define DQN_FORCE_INLINE inline __attribute__((always_inline))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: Preprocessor Token Tricks =================================================================
|
||||||
|
#define DQN_TOKEN_COMBINE2(x, y) x ## y
|
||||||
|
#define DQN_TOKEN_COMBINE(x, y) DQN_TOKEN_COMBINE2(x, y)
|
||||||
|
#define DQN_UNIQUE_NAME(prefix) DQN_TOKEN_COMBINE(prefix, __LINE__)
|
||||||
|
|
||||||
|
#define DQN_SWAP(a, b) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
auto temp = a; \
|
||||||
|
a = b; \
|
||||||
|
b = temp; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// NOTE: Size Macros ===============================================================================
|
||||||
|
#define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val)
|
||||||
|
#define DQN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0])))
|
||||||
|
#define DQN_ARRAY_ICOUNT(array) (Dqn_isize)DQN_ARRAY_UCOUNT(array)
|
||||||
|
#define DQN_CHAR_COUNT(string) (sizeof(string) - 1)
|
||||||
|
|
||||||
|
// NOTE: SI Byte Macros ============================================================================
|
||||||
|
#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))
|
||||||
|
|
||||||
|
// NOTE: Time Macros ===============================================================================
|
||||||
|
#define DQN_SECONDS_TO_MS(val) ((val) * 1000.0f)
|
||||||
|
#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)
|
||||||
|
|
||||||
|
// NOTE: Debug Macros ==============================================================================
|
||||||
|
#if !defined(DQN_DEBUG_BREAK)
|
||||||
|
#if defined(NDEBUG)
|
||||||
|
#define DQN_DEBUG_BREAK
|
||||||
|
#else
|
||||||
|
#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 <signal.h>
|
||||||
|
#define DQN_DEBUG_BREAK raise(SIGTRAP)
|
||||||
|
#elif
|
||||||
|
#error "Unhandled compiler"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_MEMSET_BYTE)
|
||||||
|
#define DQN_MEMSET_BYTE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: Assert Macros =============================================================================
|
||||||
|
#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "")
|
||||||
|
#define DQN_HARD_ASSERTF(expr, fmt, ...) \
|
||||||
|
if (!(expr)) { \
|
||||||
|
Dqn_Log_ErrorF("Hard assert triggered " #expr ". " fmt, ##__VA_ARGS__); \
|
||||||
|
DQN_DEBUG_BREAK; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(DQN_NO_ASSERT)
|
||||||
|
#define DQN_ASSERTF(...)
|
||||||
|
#define DQN_ASSERT(...)
|
||||||
|
#else
|
||||||
|
#define DQN_ASSERT(expr) DQN_ASSERTF(expr, "")
|
||||||
|
#define DQN_ASSERTF(expr, fmt, ...) \
|
||||||
|
if (!(expr)) { \
|
||||||
|
Dqn_Log_ErrorF("Assert triggered " #expr ". " fmt, ##__VA_ARGS__); \
|
||||||
|
DQN_DEBUG_BREAK; \
|
||||||
|
}
|
||||||
|
#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)
|
||||||
|
#define DQN_ZERO_INIT {}
|
||||||
|
#else
|
||||||
|
#define DQN_ZERO_INIT {0}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DQN_INVALID_CODE_PATHF(fmt, ...) DQN_ASSERTF(0, fmt, ##__VA_ARGS__)
|
||||||
|
#define DQN_INVALID_CODE_PATH DQN_INVALID_CODE_PATHF("Invalid code path triggered")
|
||||||
|
|
||||||
|
// NOTE: Defer Macro ===============================================================================
|
||||||
|
#if 0
|
||||||
|
#include <stdio.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
DQN_DEFER { printf("Three ..\n"); };
|
||||||
|
printf("One ..\n");
|
||||||
|
printf("Two ..\n");
|
||||||
|
|
||||||
|
// One ..
|
||||||
|
// Two ..
|
||||||
|
// Three ..
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename Procedure>
|
||||||
|
struct Dqn_Defer
|
||||||
|
{
|
||||||
|
Procedure proc;
|
||||||
|
Dqn_Defer(Procedure p) : proc(p) {}
|
||||||
|
~Dqn_Defer() { proc(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_DeferHelper
|
||||||
|
{
|
||||||
|
template <typename Lambda>
|
||||||
|
Dqn_Defer<Lambda> operator+(Lambda lambda) { return Dqn_Defer<Lambda>(lambda); };
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DQN_DEFER const auto DQN_UNIQUE_NAME(defer_lambda_) = Dqn_DeferHelper() + [&]()
|
||||||
|
|
||||||
|
#define DQN_DEFER_LOOP(begin, end) \
|
||||||
|
for (bool DQN_UNIQUE_NAME(once) = (begin, true); \
|
||||||
|
DQN_UNIQUE_NAME(once); \
|
||||||
|
end, DQN_UNIQUE_NAME(once) = false)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$TYPE] Typedefs | | Typedefs used in the library
|
||||||
|
// =================================================================================================
|
||||||
|
typedef intptr_t Dqn_isize;
|
||||||
|
typedef uintptr_t Dqn_usize;
|
||||||
|
typedef intptr_t Dqn_isize;
|
||||||
|
typedef float Dqn_f32;
|
||||||
|
typedef double Dqn_f64;
|
||||||
|
typedef unsigned int Dqn_uint;
|
||||||
|
typedef int32_t Dqn_b32;
|
||||||
|
|
||||||
|
#define DQN_USIZE_MAX UINTPTR_MAX
|
||||||
|
#define DQN_ISIZE_MAX INTPTR_MAX
|
||||||
|
#define DQN_ISIZE_MIN INTPTR_MIN
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$GSTR] Global Structs | | Forward declare useful structs
|
||||||
|
// =================================================================================================
|
||||||
|
struct Dqn_String8 ///< Pointer and length style UTF8 strings
|
||||||
|
{
|
||||||
|
char *data; ///< The UTF8 bytes of the string
|
||||||
|
Dqn_usize size; ///< The number of bytes in the string
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
char const *begin() const { return data; } ///< Const begin iterator for range-for loops
|
||||||
|
char const *end () const { return data + size; } ///< Const end iterator for range-for loops
|
||||||
|
char *begin() { return data; } ///< Begin iterator for range-for loops
|
||||||
|
char *end () { return data + size; } ///< End iterator for range-for loops
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
|
||||||
|
// =================================================================================================
|
||||||
|
typedef enum Dqn_ZeroMem {
|
||||||
|
Dqn_ZeroMem_No, ///< Memory can be handed out without zero-ing it out
|
||||||
|
Dqn_ZeroMem_Yes, ///< Memory should be zero-ed out before giving to the callee
|
||||||
|
} Dqn_ZeroMem;
|
||||||
|
|
||||||
|
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
|
||||||
|
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
#include <intrin.h>
|
||||||
|
#define Dqn_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
|
||||||
|
#define Dqn_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
|
||||||
|
#define Dqn_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
|
||||||
|
#define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
|
||||||
|
#define Dqn_Atomic_SubU32(target, value) Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, (long)-value)
|
||||||
|
#define Dqn_Atomic_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value)
|
||||||
|
#define Dqn_CPUClockCycle() __rdtsc()
|
||||||
|
#define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
|
||||||
|
#define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
|
||||||
|
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <x86intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Dqn_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||||
|
#define Dqn_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||||
|
#define Dqn_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||||
|
#define Dqn_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||||
|
#if defined(DQN_COMPILER_GCC)
|
||||||
|
#define Dqn_CPUClockCycle() __rdtsc()
|
||||||
|
#else
|
||||||
|
#define Dqn_CPUClockCycle() __builtin_readcyclecounter()
|
||||||
|
#endif
|
||||||
|
#define Dqn_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory")
|
||||||
|
#define Dqn_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory")
|
||||||
|
#else
|
||||||
|
#error "Compiler not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Atomically set the value into the target using an atomic compare and swap.
|
||||||
|
/// @param[in,out] target The target pointer to set atomically
|
||||||
|
/// @param[in] value The value to set atomically into the target
|
||||||
|
/// @return The value that was last stored in the target
|
||||||
|
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||||
|
{
|
||||||
|
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
__int64 result;
|
||||||
|
do {
|
||||||
|
result = *target;
|
||||||
|
} while (Dqn_Atomic_CompareExchange64(target, value, result) != result);
|
||||||
|
return DQN_CAST(uint64_t)result;
|
||||||
|
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||||
|
uint64_t result = __sync_lock_test_and_set(target, value);
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
#error Unsupported compiler
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Atomically set the value into the target using an atomic compare and swap.
|
||||||
|
/// @param[in,out] target The target pointer to set atomically
|
||||||
|
/// @param[in] value The value to set atomically into the target
|
||||||
|
/// @return The value that was last stored in the target
|
||||||
|
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
|
||||||
|
{
|
||||||
|
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
long result;
|
||||||
|
do {
|
||||||
|
result = *target;
|
||||||
|
} while (Dqn_Atomic_CompareExchange32(target, value, result) != result);
|
||||||
|
return result;
|
||||||
|
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||||
|
long result = __sync_lock_test_and_set(target, value);
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
#error Unsupported compiler
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(DQN_OS_ARM64)
|
||||||
|
struct Dqn_CPUIDRegisters
|
||||||
|
{
|
||||||
|
Dqn_uint array[4]; ///< Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Execute 'CPUID' instruction to query the capabilities of the current CPU.
|
||||||
|
Dqn_CPUIDRegisters Dqn_CPUID(int function_id);
|
||||||
|
#endif // DQN_OS_ARM64
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
|
||||||
|
// =================================================================================================
|
||||||
|
// A mutex implemented using an atomic compare and swap on tickets handed out
|
||||||
|
// for each critical section.
|
||||||
|
//
|
||||||
|
// This mutex serves ticket in order and will block all other threads until the
|
||||||
|
// tickets are returned in order. The thread with the oldest ticket that has
|
||||||
|
// not been returned has right of way to execute, all other threads will be
|
||||||
|
// blocked in an atomic compare and swap loop. block execution by going into an
|
||||||
|
// atomic
|
||||||
|
//
|
||||||
|
// When a thread is blocked by this mutex, a spinlock intrinsic `_mm_pause` is
|
||||||
|
// used to yield the CPU and reduce spinlock on the thread. This mutex is not
|
||||||
|
// ideal for long blocking operations. This mutex does not issue any syscalls
|
||||||
|
// and relies entirely on atomic instructions.
|
||||||
|
//
|
||||||
|
// NOTE: API
|
||||||
|
//
|
||||||
|
// @proc Dqn_TicketMutex_Begin, End
|
||||||
|
// @desc Lock and unlock the mutex respectively
|
||||||
|
//
|
||||||
|
// @proc Dqn_TicketMutex_MakeTicket
|
||||||
|
// @desc Allocate the next available ticket from the mutex for locking using
|
||||||
|
// Dqn_TicketMutex_BeginTicket().
|
||||||
|
// @param[in] mutex The mutex
|
||||||
|
// @code
|
||||||
|
// 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);
|
||||||
|
// @endcode
|
||||||
|
//
|
||||||
|
// @proc Dqn_TicketMutex_BeginTicket
|
||||||
|
// @desc Lock the mutex using the given ticket if possible, otherwise block
|
||||||
|
// waiting until the mutex can be locked.
|
||||||
|
//
|
||||||
|
// @proc Dqn_TicketMutex_CanLock
|
||||||
|
// @desc Determine if the mutex can be locked using the given ticket number
|
||||||
|
|
||||||
|
struct Dqn_TicketMutex
|
||||||
|
{
|
||||||
|
unsigned int volatile ticket; ///< The next ticket to give out to the thread taking the mutex
|
||||||
|
unsigned int volatile serving; ///< The ticket ID to block the mutex on until it is returned
|
||||||
|
};
|
||||||
|
|
||||||
|
void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
|
||||||
|
void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
|
||||||
|
Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
|
||||||
|
void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||||
|
bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$CALL] Dqn_CallSite | | Source code location/tracing
|
||||||
|
// =================================================================================================
|
||||||
|
typedef struct Dqn_CallSite {
|
||||||
|
Dqn_String8 file;
|
||||||
|
Dqn_String8 function;
|
||||||
|
unsigned int line;
|
||||||
|
} Dqn_CalSite;
|
||||||
|
|
||||||
|
#define DQN_CALL_SITE Dqn_CallSite{DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__}
|
149
dqn_curl.h
149
dqn_curl.h
@ -1,149 +0,0 @@
|
|||||||
#ifndef DQN_CURL_H
|
|
||||||
#define DQN_CURL_H
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Dqn_Curl
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Define DQN_CURL_IMPLEMENTATION in one and only one file to enable the
|
|
||||||
// implementation in translation unit.
|
|
||||||
//
|
|
||||||
// curl_global_init(CURL_GLOBAL_ALL) must return CURLE_OK before anything
|
|
||||||
// in this file is used. You may cleanup curl on exit via curl_global_cleanup()
|
|
||||||
// if desired.
|
|
||||||
//
|
|
||||||
// An easy way to generate the curl commands required to query a url is to use
|
|
||||||
// CURL itself and the option to dump the command to a C-compatible file, i.e.
|
|
||||||
//
|
|
||||||
// curl --libcurl RequestToCCode.c -X POST -H "Content-Type: application/json" --data-binary "{\"jsonrpc\": \"2.0\", \"id\": \"0\", \"method\": \"get_block_count\", \"params\": []}" oxen.observer:22023/json_rpc
|
|
||||||
//
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Example
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
#if 0
|
|
||||||
struct CurlWriteFunctionUserData
|
|
||||||
{
|
|
||||||
Dqn_ArenaAllocator *arena;
|
|
||||||
Dqn_StringList data;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t CurlWriteFunction(char *ptr, size_t size, size_t nmemb, void *userdata)
|
|
||||||
{
|
|
||||||
auto *user_data = DQN_CAST(CurlWriteFunctionUserData *)userdata;
|
|
||||||
Dqn_StringList_AppendStringCopy(&user_data->data, user_data->arena, Dqn_String_Init(ptr, nmemb));
|
|
||||||
DQN_ASSERT(size == 1);
|
|
||||||
return nmemb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// NOTE: Setup Curl handle
|
|
||||||
CURL *handle = curl_easy_init();
|
|
||||||
|
|
||||||
struct curl_slist *header_list = nullptr;
|
|
||||||
header_list = curl_slist_append(header_list, "Content-Type: application/json");
|
|
||||||
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list);
|
|
||||||
|
|
||||||
Dqn_String post_data = DQN_STRING("{\"jsonrpc\": \"2.0\", \"id\": \"0\", \"method\": \"get_block_count\", \"params\": []}");
|
|
||||||
Dqn_Curl_SetHTTPPost(handle, "oxen.observer:22023/json_rpc", post_data.str, DQN_CAST(int)post_data.size);
|
|
||||||
|
|
||||||
// NOTE: Set write callback
|
|
||||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch();
|
|
||||||
CurlWriteFunctionUserData user_data = {};
|
|
||||||
user_data.arena = scratch.arena;
|
|
||||||
Dqn_Curl_SetWriteCallback(handle, CurlWriteFunction, &user_data);
|
|
||||||
|
|
||||||
// NOTE: Execute CURL query
|
|
||||||
curl_easy_perform(handle);
|
|
||||||
Dqn_String output = Dqn_StringList_Build(&user_data.string_list, scoped_arena.arena);
|
|
||||||
DQN_LOG_I("%.*s", DQN_STRING_FMT(output));
|
|
||||||
|
|
||||||
// NOTE: Cleanup
|
|
||||||
curl_slist_free_all(header_list);
|
|
||||||
curl_easy_cleanup(handle);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// NOTE: Warning this causes Windows.h to be included.
|
|
||||||
#define CURL_STATICLIB
|
|
||||||
#define NOMINMAX
|
|
||||||
#include <curl-7.72.0/include/curl/curl.h>
|
|
||||||
|
|
||||||
struct Dqn_CurlProcs
|
|
||||||
{
|
|
||||||
CURL *(*curl_easy_init)(void);
|
|
||||||
CURLcode (*curl_easy_getinfo)(CURL *curl, CURLINFO info, ...);
|
|
||||||
CURLcode (*curl_easy_setopt)(CURL *handle, CURLoption option, ...);
|
|
||||||
const char *(*curl_easy_strerror)(CURLcode);
|
|
||||||
void (*curl_easy_cleanup)(CURL *curl);
|
|
||||||
|
|
||||||
CURLM *(*curl_multi_init)(void);
|
|
||||||
CURLMcode (*curl_multi_perform)(CURLM *multi_handle, int *running_handles);
|
|
||||||
CURLMsg *(*curl_multi_info_read)(CURLM *multi_handle, int *msgs_in_queue);
|
|
||||||
CURLMcode (*curl_multi_remove_handle)(CURLM *multi_handle, CURL *curl_handle);
|
|
||||||
CURLMcode (*curl_multi_add_handle)(CURLM *multi_handle, CURL *curl_handle);
|
|
||||||
const char *(*curl_multi_strerror)(CURLMcode);
|
|
||||||
|
|
||||||
struct curl_slist *(*curl_slist_append)(struct curl_slist *, const char *);
|
|
||||||
void (*curl_slist_free_all)(struct curl_slist *);
|
|
||||||
};
|
|
||||||
|
|
||||||
Dqn_CurlProcs Dqn_CurlProcs_Init();
|
|
||||||
void Dqn_Curl_SetURL(CURL *handle, char const *url);
|
|
||||||
void Dqn_Curl_SetHTTPPost(CURL *handle, char const *url, char const *post_data, int post_data_size);
|
|
||||||
void Dqn_Curl_SetWriteCallback(CURL *handle, size_t (*curl_write_callback)(char *ptr, size_t size, size_t nmemb, void *userdata), void *user_data);
|
|
||||||
#endif // DQN_CURL_H
|
|
||||||
|
|
||||||
#if defined(DQN_CURL_IMPLEMENTATION)
|
|
||||||
#pragma comment (lib, "Advapi32")
|
|
||||||
#pragma comment (lib, "Crypt32")
|
|
||||||
#pragma comment (lib, "Ws2_32")
|
|
||||||
#pragma comment (lib, "Wldap32")
|
|
||||||
#pragma comment (lib, "Normaliz")
|
|
||||||
|
|
||||||
Dqn_CurlProcs Dqn_CurlProcs_Init()
|
|
||||||
{
|
|
||||||
Dqn_CurlProcs result = {};
|
|
||||||
result.curl_easy_init = curl_easy_init;
|
|
||||||
result.curl_easy_getinfo = curl_easy_getinfo;
|
|
||||||
result.curl_easy_setopt = curl_easy_setopt;
|
|
||||||
result.curl_easy_strerror = curl_easy_strerror;
|
|
||||||
result.curl_easy_cleanup = curl_easy_cleanup;
|
|
||||||
|
|
||||||
result.curl_multi_init = curl_multi_init;
|
|
||||||
result.curl_multi_perform = curl_multi_perform;
|
|
||||||
result.curl_multi_info_read = curl_multi_info_read;
|
|
||||||
result.curl_multi_remove_handle = curl_multi_remove_handle;
|
|
||||||
result.curl_multi_add_handle = curl_multi_add_handle;
|
|
||||||
result.curl_multi_strerror = curl_multi_strerror;
|
|
||||||
|
|
||||||
result.curl_slist_append = curl_slist_append;
|
|
||||||
result.curl_slist_free_all = curl_slist_free_all;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Curl_SetURL(CURL *handle, char const *url)
|
|
||||||
{
|
|
||||||
curl_easy_setopt(handle, CURLOPT_URL, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Curl_SetHTTPPost(CURL *handle, char const *url, char const *post_data, int post_data_size)
|
|
||||||
{
|
|
||||||
curl_easy_setopt(handle, CURLOPT_BUFFERSIZE, 102400L);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_URL, url);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, post_data);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)post_data_size);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, "curl/7.55.1");
|
|
||||||
curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 50L);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "POST");
|
|
||||||
curl_easy_setopt(handle, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dqn_Curl_SetWriteCallback(CURL *handle,
|
|
||||||
size_t (*curl_write_callback)(char *ptr, size_t size, size_t nmemb, void *userdata),
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_callback);
|
|
||||||
curl_easy_setopt(handle, CURLOPT_WRITEDATA, user_data);
|
|
||||||
}
|
|
||||||
#endif // DQN_CURL_IMPLEMENTATION
|
|
251
dqn_hash.cpp
Normal file
251
dqn_hash.cpp
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
|
||||||
|
// =================================================================================================
|
||||||
|
// Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/
|
||||||
|
DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_usize size, uint32_t hash)
|
||||||
|
{
|
||||||
|
auto buffer = DQN_CAST(uint8_t const *)bytes;
|
||||||
|
for (Dqn_usize i = 0; i < size; i++)
|
||||||
|
hash = (buffer[i] ^ hash) * 16777619 /*FNV Prime*/;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_FNV1A32_Hash(void const *bytes, Dqn_usize size)
|
||||||
|
{
|
||||||
|
uint32_t result = Dqn_FNV1A32_Iterate(bytes, size, DQN_FNV1A32_SEED);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_FNV1A64_Iterate(void const *bytes, Dqn_usize size, uint64_t hash)
|
||||||
|
{
|
||||||
|
auto buffer = DQN_CAST(uint8_t const *)bytes;
|
||||||
|
for (Dqn_usize i = 0; i < size; i++)
|
||||||
|
hash = (buffer[i] ^ hash) * 1099511628211 /*FNV Prime*/;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_FNV1A64_Hash(void const *bytes, Dqn_usize size)
|
||||||
|
{
|
||||||
|
uint64_t result = Dqn_FNV1A64_Iterate(bytes, size, DQN_FNV1A64_SEED);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
|
||||||
|
// =================================================================================================
|
||||||
|
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
#define DQN_MMH3_ROTL32(x, y) _rotl(x, y)
|
||||||
|
#define DQN_MMH3_ROTL64(x, y) _rotl64(x, y)
|
||||||
|
#else
|
||||||
|
#define DQN_MMH3_ROTL32(x, y) ((x) << (y)) | ((x) >> (32 - (y)))
|
||||||
|
#define DQN_MMH3_ROTL64(x, y) ((x) << (y)) | ((x) >> (64 - (y)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Block read - if your platform needs to do endian-swapping or can only
|
||||||
|
// handle aligned reads, do the conversion here
|
||||||
|
|
||||||
|
DQN_FORCE_INLINE uint32_t Dqn_MurmurHash3_GetBlock32(uint32_t const *p, int i)
|
||||||
|
{
|
||||||
|
return p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_FORCE_INLINE uint64_t Dqn_MurmurHash3_GetBlock64(uint64_t const *p, int i)
|
||||||
|
{
|
||||||
|
return p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Finalization mix - force all bits of a hash block to avalanche
|
||||||
|
|
||||||
|
DQN_FORCE_INLINE uint32_t Dqn_MurmurHash3_FMix32(uint32_t h)
|
||||||
|
{
|
||||||
|
h ^= h >> 16;
|
||||||
|
h *= 0x85ebca6b;
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= 0xc2b2ae35;
|
||||||
|
h ^= h >> 16;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_FORCE_INLINE uint64_t Dqn_MurmurHash3_FMix64(uint64_t k)
|
||||||
|
{
|
||||||
|
k ^= k >> 33;
|
||||||
|
k *= 0xff51afd7ed558ccd;
|
||||||
|
k ^= k >> 33;
|
||||||
|
k *= 0xc4ceb9fe1a85ec53;
|
||||||
|
k ^= k >> 33;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_MurmurHash3_x86U32(void const *key, int len, uint32_t seed)
|
||||||
|
{
|
||||||
|
const uint8_t *data = (const uint8_t *)key;
|
||||||
|
const int nblocks = len / 4;
|
||||||
|
|
||||||
|
uint32_t h1 = seed;
|
||||||
|
|
||||||
|
const uint32_t c1 = 0xcc9e2d51;
|
||||||
|
const uint32_t c2 = 0x1b873593;
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// body
|
||||||
|
|
||||||
|
const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
|
||||||
|
|
||||||
|
for (int i = -nblocks; i; i++)
|
||||||
|
{
|
||||||
|
uint32_t k1 = Dqn_MurmurHash3_GetBlock32(blocks, i);
|
||||||
|
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = DQN_MMH3_ROTL32(k1, 15);
|
||||||
|
k1 *= c2;
|
||||||
|
|
||||||
|
h1 ^= k1;
|
||||||
|
h1 = DQN_MMH3_ROTL32(h1, 13);
|
||||||
|
h1 = h1 * 5 + 0xe6546b64;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// tail
|
||||||
|
|
||||||
|
const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
|
||||||
|
|
||||||
|
uint32_t k1 = 0;
|
||||||
|
|
||||||
|
switch (len & 3)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
k1 ^= tail[2] << 16;
|
||||||
|
case 2:
|
||||||
|
k1 ^= tail[1] << 8;
|
||||||
|
case 1:
|
||||||
|
k1 ^= tail[0];
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = DQN_MMH3_ROTL32(k1, 15);
|
||||||
|
k1 *= c2;
|
||||||
|
h1 ^= k1;
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// finalization
|
||||||
|
|
||||||
|
h1 ^= len;
|
||||||
|
|
||||||
|
h1 = Dqn_MurmurHash3_FMix32(h1);
|
||||||
|
|
||||||
|
return h1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32_t seed)
|
||||||
|
{
|
||||||
|
const uint8_t *data = (const uint8_t *)key;
|
||||||
|
const int nblocks = len / 16;
|
||||||
|
|
||||||
|
uint64_t h1 = seed;
|
||||||
|
uint64_t h2 = seed;
|
||||||
|
|
||||||
|
const uint64_t c1 = 0x87c37b91114253d5;
|
||||||
|
const uint64_t c2 = 0x4cf5ad432745937f;
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// body
|
||||||
|
|
||||||
|
const uint64_t *blocks = (const uint64_t *)(data);
|
||||||
|
|
||||||
|
for (int i = 0; i < nblocks; i++)
|
||||||
|
{
|
||||||
|
uint64_t k1 = Dqn_MurmurHash3_GetBlock64(blocks, i * 2 + 0);
|
||||||
|
uint64_t k2 = Dqn_MurmurHash3_GetBlock64(blocks, i * 2 + 1);
|
||||||
|
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = DQN_MMH3_ROTL64(k1, 31);
|
||||||
|
k1 *= c2;
|
||||||
|
h1 ^= k1;
|
||||||
|
|
||||||
|
h1 = DQN_MMH3_ROTL64(h1, 27);
|
||||||
|
h1 += h2;
|
||||||
|
h1 = h1 * 5 + 0x52dce729;
|
||||||
|
|
||||||
|
k2 *= c2;
|
||||||
|
k2 = DQN_MMH3_ROTL64(k2, 33);
|
||||||
|
k2 *= c1;
|
||||||
|
h2 ^= k2;
|
||||||
|
|
||||||
|
h2 = DQN_MMH3_ROTL64(h2, 31);
|
||||||
|
h2 += h1;
|
||||||
|
h2 = h2 * 5 + 0x38495ab5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// tail
|
||||||
|
|
||||||
|
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
|
||||||
|
|
||||||
|
uint64_t k1 = 0;
|
||||||
|
uint64_t k2 = 0;
|
||||||
|
|
||||||
|
switch (len & 15)
|
||||||
|
{
|
||||||
|
case 15:
|
||||||
|
k2 ^= ((uint64_t)tail[14]) << 48;
|
||||||
|
case 14:
|
||||||
|
k2 ^= ((uint64_t)tail[13]) << 40;
|
||||||
|
case 13:
|
||||||
|
k2 ^= ((uint64_t)tail[12]) << 32;
|
||||||
|
case 12:
|
||||||
|
k2 ^= ((uint64_t)tail[11]) << 24;
|
||||||
|
case 11:
|
||||||
|
k2 ^= ((uint64_t)tail[10]) << 16;
|
||||||
|
case 10:
|
||||||
|
k2 ^= ((uint64_t)tail[9]) << 8;
|
||||||
|
case 9:
|
||||||
|
k2 ^= ((uint64_t)tail[8]) << 0;
|
||||||
|
k2 *= c2;
|
||||||
|
k2 = DQN_MMH3_ROTL64(k2, 33);
|
||||||
|
k2 *= c1;
|
||||||
|
h2 ^= k2;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
k1 ^= ((uint64_t)tail[7]) << 56;
|
||||||
|
case 7:
|
||||||
|
k1 ^= ((uint64_t)tail[6]) << 48;
|
||||||
|
case 6:
|
||||||
|
k1 ^= ((uint64_t)tail[5]) << 40;
|
||||||
|
case 5:
|
||||||
|
k1 ^= ((uint64_t)tail[4]) << 32;
|
||||||
|
case 4:
|
||||||
|
k1 ^= ((uint64_t)tail[3]) << 24;
|
||||||
|
case 3:
|
||||||
|
k1 ^= ((uint64_t)tail[2]) << 16;
|
||||||
|
case 2:
|
||||||
|
k1 ^= ((uint64_t)tail[1]) << 8;
|
||||||
|
case 1:
|
||||||
|
k1 ^= ((uint64_t)tail[0]) << 0;
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = DQN_MMH3_ROTL64(k1, 31);
|
||||||
|
k1 *= c2;
|
||||||
|
h1 ^= k1;
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// finalization
|
||||||
|
|
||||||
|
h1 ^= len;
|
||||||
|
h2 ^= len;
|
||||||
|
|
||||||
|
h1 += h2;
|
||||||
|
h2 += h1;
|
||||||
|
|
||||||
|
h1 = Dqn_MurmurHash3_FMix64(h1);
|
||||||
|
h2 = Dqn_MurmurHash3_FMix64(h2);
|
||||||
|
|
||||||
|
h1 += h2;
|
||||||
|
h2 += h1;
|
||||||
|
|
||||||
|
Dqn_MurmurHash3 result = {};
|
||||||
|
result.e[0] = h1;
|
||||||
|
result.e[1] = h2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
40
dqn_hash.h
Normal file
40
dqn_hash.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
|
||||||
|
// =================================================================================================
|
||||||
|
#ifndef DQN_FNV1A32_SEED
|
||||||
|
#define DQN_FNV1A32_SEED 2166136261U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DQN_FNV1A64_SEED
|
||||||
|
#define DQN_FNV1A64_SEED 14695981039346656037ULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @begincode
|
||||||
|
/// char buffer1[128] = {random bytes};
|
||||||
|
/// char buffer2[128] = {random bytes};
|
||||||
|
/// uint64_t hash = Dqn_FNV1A64_Hash(buffer1, sizeof(buffer1));
|
||||||
|
/// hash = Dqn_FNV1A64_Iterate(buffer2, sizeof(buffer2), hash); // subsequent hashing
|
||||||
|
/// @endcode
|
||||||
|
DQN_API uint32_t Dqn_FNV1A32_Hash (void const *bytes, Dqn_usize size);
|
||||||
|
DQN_API uint64_t Dqn_FNV1A64_Hash (void const *bytes, Dqn_usize size);
|
||||||
|
DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_usize size, uint32_t hash);
|
||||||
|
DQN_API uint64_t Dqn_FNV1A64_Iterate(void const *bytes, Dqn_usize size, uint64_t hash);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
|
||||||
|
// =================================================================================================
|
||||||
|
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||||
|
// domain. The author (Austin Appleby) hereby disclaims copyright to this source
|
||||||
|
// code.
|
||||||
|
//
|
||||||
|
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||||
|
// algorithms are optimized for their respective platforms. You can still
|
||||||
|
// compile and run any of them on any platform, but your performance with the
|
||||||
|
// non-native version will be less than optimal.
|
||||||
|
struct Dqn_MurmurHash3 { uint64_t e[2]; };
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed);
|
||||||
|
DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32_t seed);
|
||||||
|
#define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
|
||||||
|
#define Dqn_MurmurHash3_x64U128AsU32(key, len, seed) (DQN_CAST(uint32_t)Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
|
||||||
|
|
450
dqn_math.cpp
Normal file
450
dqn_math.cpp
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
#if !defined(DQN_NO_MATH)
|
||||||
|
// =================================================================================================
|
||||||
|
// [$MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API Dqn_V2I Dqn_V2ToV2I(Dqn_V2 a)
|
||||||
|
{
|
||||||
|
Dqn_V2I result = Dqn_V2I(DQN_CAST(int32_t)a.x, DQN_CAST(int32_t)a.y);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Min(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_V2Max(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_V2Abs(Dqn_V2 a)
|
||||||
|
{
|
||||||
|
Dqn_V2 result = Dqn_V2(DQN_ABS(a.x), DQN_ABS(a.y));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_f32 Dqn_V2Dot(Dqn_V2 a, Dqn_V2 b)
|
||||||
|
{
|
||||||
|
Dqn_f32 result = (a.x * b.x) + (a.y * b.y);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_f32 Dqn_V2LengthSq(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_V2Normalise(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_V2Perpendicular(Dqn_V2 a)
|
||||||
|
{
|
||||||
|
Dqn_V2 result = Dqn_V2(-a.y, a.x);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_V3 Implementation
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
DQN_API Dqn_f32 Dqn_V3LengthSq(Dqn_V3 a)
|
||||||
|
{
|
||||||
|
Dqn_f32 result = DQN_SQUARED(a.x) + DQN_SQUARED(a.y) + DQN_SQUARED(a.z);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_f32 Dqn_V3Length(Dqn_V3 a)
|
||||||
|
{
|
||||||
|
Dqn_f32 length_sq = DQN_SQUARED(a.x) + DQN_SQUARED(a.y) + DQN_SQUARED(a.z);
|
||||||
|
Dqn_f32 result = DQN_SQRTF(length_sq);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_V3 Dqn_V3Normalise(Dqn_V3 a)
|
||||||
|
{
|
||||||
|
Dqn_f32 length = Dqn_V3Length(a);
|
||||||
|
Dqn_V3 result = a / length;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_V4 Implementation
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
DQN_API Dqn_f32 Dqn_V4Dot(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_ASSERTF(DQN_ABS(Dqn_V3Length(axis01) - 1.f) <= 0.01f,
|
||||||
|
"Rotation axis must be normalised, length = %f",
|
||||||
|
Dqn_V3Length(axis01));
|
||||||
|
|
||||||
|
Dqn_f32 sin = DQN_SINF(radians);
|
||||||
|
Dqn_f32 cos = DQN_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_SQUARED(x);
|
||||||
|
Dqn_f32 y2 = DQN_SQUARED(y);
|
||||||
|
Dqn_f32 z2 = DQN_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)
|
||||||
|
{
|
||||||
|
// NOTE: Here is the matrix in column major for readability. Below it's
|
||||||
|
// transposed due to how you have to declare column major matrices in C/C++.
|
||||||
|
//
|
||||||
|
// m = [2/r-l, 0, 0, -1*(r+l)/(r-l)]
|
||||||
|
// [0, 2/t-b, 0, 1*(t+b)/(t-b)]
|
||||||
|
// [0, 0, -2/f-n, -1*(f+n)/(f-n)]
|
||||||
|
// [0, 0, 0, 1 ]
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(DQN_NO_FSTRING8)
|
||||||
|
DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat)
|
||||||
|
{
|
||||||
|
Dqn_FString8<256> result = {};
|
||||||
|
for (int row = 0; row < 4; row++) {
|
||||||
|
for (int it = 0; it < 4; it++) {
|
||||||
|
if (it == 0) Dqn_FString8_Append(&result, DQN_STRING8("|"));
|
||||||
|
Dqn_FString8_AppendF(&result, "%.5f", mat.columns[it][row]);
|
||||||
|
if (it != 3) Dqn_FString8_Append(&result, DQN_STRING8(", "));
|
||||||
|
else Dqn_FString8_Append(&result, DQN_STRING8("|\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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_V2Abs(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 bool Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p)
|
||||||
|
{
|
||||||
|
bool 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 bool Dqn_Rect_ContainsRect(Dqn_Rect a, Dqn_Rect b)
|
||||||
|
{
|
||||||
|
bool 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 bool Dqn_Rect_Intersects(Dqn_Rect a, Dqn_Rect b)
|
||||||
|
{
|
||||||
|
bool 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_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 a, Dqn_Rect 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 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_Lerp_V2(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_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b)
|
||||||
|
{
|
||||||
|
Dqn_f32 result = a + ((b - a) * t);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif // !defined(DQN_NO_MATH)
|
||||||
|
|
215
dqn_math.h
Normal file
215
dqn_math.h
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp
|
||||||
|
// =================================================================================================
|
||||||
|
#if !defined(DQN_NO_MATH)
|
||||||
|
struct Dqn_V2I
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
|
||||||
|
Dqn_V2I() = default;
|
||||||
|
Dqn_V2I(Dqn_f32 x_, Dqn_f32 y_): x((int32_t)x_), y((int32_t)y_) {}
|
||||||
|
Dqn_V2I(int32_t x_, int32_t y_): x(x_), y(y_) {}
|
||||||
|
Dqn_V2I(int32_t 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* (int32_t 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/ (int32_t 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*=(int32_t 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(int32_t a) : x((Dqn_f32)a), y((Dqn_f32)a) {}
|
||||||
|
Dqn_V2(Dqn_f32 x, Dqn_f32 y): x(x), y(y) {}
|
||||||
|
Dqn_V2(int32_t x, int32_t 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* (int32_t 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/ (int32_t 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*=(int32_t 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/=(int32_t 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(int32_t 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(int32_t x, int32_t 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* (int32_t 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/ (int32_t 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*=(int32_t 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/=(int32_t 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(int32_t x, int32_t y, int32_t z, int32_t 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* (int32_t 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*=(int32_t 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_V2ToV2I(Dqn_V2 a);
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Min(Dqn_V2 a, Dqn_V2 b);
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Max(Dqn_V2 a, Dqn_V2 b);
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Abs(Dqn_V2 a);
|
||||||
|
DQN_API Dqn_f32 Dqn_V2Dot(Dqn_V2 a, Dqn_V2 b);
|
||||||
|
DQN_API Dqn_f32 Dqn_V2LengthSq(Dqn_V2 a, Dqn_V2 b);
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Normalise(Dqn_V2 a);
|
||||||
|
DQN_API Dqn_V2 Dqn_V2Perpendicular(Dqn_V2 a);
|
||||||
|
|
||||||
|
DQN_API Dqn_f32 Dqn_V3LengthSq(Dqn_V3 a);
|
||||||
|
DQN_API Dqn_f32 Dqn_V3Length(Dqn_V3 a);
|
||||||
|
DQN_API Dqn_V3 Dqn_V3Normalise(Dqn_V3 a);
|
||||||
|
|
||||||
|
DQN_API Dqn_f32 Dqn_V4Dot(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);
|
||||||
|
|
||||||
|
#if !defined(DQN_NO_FSTRING8)
|
||||||
|
DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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) {}
|
||||||
|
bool 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 bool Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p);
|
||||||
|
DQN_API bool 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 bool 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);
|
||||||
|
|
||||||
|
DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b);
|
||||||
|
DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b);
|
||||||
|
#endif // !defined(DQN_NO_MATH)
|
||||||
|
|
641
dqn_memory.cpp
Normal file
641
dqn_memory.cpp
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$ALLO] Dqn_Allocator | | Generic allocator interface
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void *Dqn_Allocator_Alloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||||
|
{
|
||||||
|
void *result = NULL;
|
||||||
|
if (allocator.alloc) {
|
||||||
|
result = allocator.alloc(DQN_LEAK_TRACE_ARG size, align, zero_mem, allocator.user_context);
|
||||||
|
} else {
|
||||||
|
result = DQN_ALLOC(size);
|
||||||
|
Dqn_Library_LeakTraceAdd(DQN_LEAK_TRACE_ARG result, size);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
if (allocator.dealloc) {
|
||||||
|
allocator.dealloc(DQN_LEAK_TRACE_ARG ptr, size, allocator.user_context);
|
||||||
|
} else {
|
||||||
|
DQN_DEALLOC(ptr, size);
|
||||||
|
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$VMEM] Dqn_VMem | | Virtual memory allocation
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_FILE_SCOPE uint32_t Dqn_VMem_ConvertPageToOSFlags_(uint32_t protect)
|
||||||
|
{
|
||||||
|
DQN_ASSERT((protect & ~(Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard)) == 0);
|
||||||
|
DQN_ASSERT(protect != 0);
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
if (protect & Dqn_VMemPage_NoAccess) {
|
||||||
|
result = PAGE_NOACCESS;
|
||||||
|
} else {
|
||||||
|
if (protect & Dqn_VMemPage_ReadWrite) {
|
||||||
|
result = PAGE_READWRITE;
|
||||||
|
} else if (protect & Dqn_VMemPage_Read) {
|
||||||
|
result = PAGE_READONLY;
|
||||||
|
} else if (protect & Dqn_VMemPage_Write) {
|
||||||
|
Dqn_Log_WarningF("Windows does not support write-only pages, granting read+write access");
|
||||||
|
result = PAGE_READWRITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protect & Dqn_VMemPage_Guard)
|
||||||
|
result |= PAGE_GUARD;
|
||||||
|
|
||||||
|
DQN_ASSERTF(result != PAGE_GUARD, "Page guard is a modifier, you must also specify a page permission like read or/and write");
|
||||||
|
#else
|
||||||
|
if (protect & (Dqn_VMemPage_NoAccess | Dqn_VMemPage_Guard)) {
|
||||||
|
result = PROT_NONE;
|
||||||
|
} else {
|
||||||
|
if (protect & Dqn_VMemPage_Read)
|
||||||
|
result = PROT_READ;
|
||||||
|
if (protect & Dqn_VMemPage_Write)
|
||||||
|
result = PROT_WRITE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void *Dqn_VMem_Reserve(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags)
|
||||||
|
{
|
||||||
|
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||||
|
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
unsigned long flags = MEM_RESERVE | (commit == Dqn_VMemCommit_Yes ? MEM_COMMIT : 0);
|
||||||
|
void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
|
||||||
|
|
||||||
|
#elif defined(DQN_OS_UNIX)
|
||||||
|
if (commit == Dqn_VMemCommit_Yes)
|
||||||
|
os_page_flags |= (PROT_READ | PROT_WRITE);
|
||||||
|
|
||||||
|
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (result == MAP_FAILED)
|
||||||
|
result = nullptr;
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Missing implementation for Dqn_VMem_Reserve"
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_VMem_Commit(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||||
|
{
|
||||||
|
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
bool result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
||||||
|
#elif defined(DQN_OS_UNIX)
|
||||||
|
bool result = mprotect(ptr, size, os_page_flags) == 0;
|
||||||
|
#else
|
||||||
|
#error "Missing implementation for Dqn_VMem_Commit"
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size)
|
||||||
|
{
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
VirtualFree(ptr, size, MEM_DECOMMIT);
|
||||||
|
#elif defined(DQN_OS_UNIX)
|
||||||
|
mprotect(ptr, size, PROT_NONE);
|
||||||
|
madvise(ptr, size, MADV_FREE);
|
||||||
|
#else
|
||||||
|
#error "Missing implementation for Dqn_VMem_Decommit"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_VMem_Release(void *ptr, Dqn_usize size)
|
||||||
|
{
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
(void)size;
|
||||||
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||||
|
#elif defined(DQN_OS_UNIX)
|
||||||
|
munmap(ptr, size);
|
||||||
|
#else
|
||||||
|
#error "Missing implementation for Dqn_VMem_Release"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||||
|
{
|
||||||
|
if (!ptr || size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
static Dqn_String8 const ALIGNMENT_ERROR_MSG =
|
||||||
|
DQN_STRING8("Page protection requires pointers to be page aligned because we "
|
||||||
|
"can only guard memory at a multiple of the page boundary.");
|
||||||
|
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, DQN_VMEM_PAGE_GRANULARITY), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||||
|
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, DQN_VMEM_PAGE_GRANULARITY), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||||
|
|
||||||
|
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
unsigned long prev_flags = 0;
|
||||||
|
int result = VirtualProtect(ptr, size, os_page_flags, &prev_flags);
|
||||||
|
(void)prev_flags;
|
||||||
|
if (result == 0) {
|
||||||
|
Dqn_WinErrorMsg error = Dqn_Win_LastError();
|
||||||
|
DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, error.size, error.data);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int result = mprotect(result->memory, result->size, os_page_flags);
|
||||||
|
DQN_ASSERTF(result == 0, "mprotect failed (%d)", errno);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$AREN] Dqn_Arena | | Growing bump allocator
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void Dqn_Arena_CommitFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, Dqn_ArenaCommit commit)
|
||||||
|
{
|
||||||
|
Dqn_usize commit_size = 0;
|
||||||
|
switch (commit) {
|
||||||
|
case Dqn_ArenaCommit_GetNewPages: {
|
||||||
|
commit_size = Dqn_PowerOfTwoAlign(size, DQN_VMEM_COMMIT_GRANULARITY);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Dqn_ArenaCommit_EnsureSpace: {
|
||||||
|
DQN_ASSERT(block->commit >= block->used);
|
||||||
|
Dqn_usize const unused_commit_space = block->commit - block->used;
|
||||||
|
if (unused_commit_space < size)
|
||||||
|
commit_size = Dqn_PowerOfTwoAlign(size - unused_commit_space, DQN_VMEM_COMMIT_GRANULARITY);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commit_size) {
|
||||||
|
uint32_t page_flags = Dqn_VMemPage_ReadWrite;
|
||||||
|
char *commit_ptr = DQN_CAST(char *)block->memory + block->commit;
|
||||||
|
Dqn_VMem_Commit(commit_ptr, commit_size, page_flags);
|
||||||
|
block->commit += commit_size;
|
||||||
|
|
||||||
|
DQN_ASSERTF(block->commit < block->size,
|
||||||
|
"Block size should be PoT aligned and its alignment should be greater than the commit granularity [block_size=%_$$d, block_commit=%_$$d]",
|
||||||
|
block->size, block->commit);
|
||||||
|
|
||||||
|
// NOTE: Guard new pages being allocated from the block
|
||||||
|
if (block->arena->use_after_free_guard)
|
||||||
|
Dqn_VMem_Protect(commit_ptr, commit_size, page_flags | Dqn_VMemPage_Guard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void *Dqn_Arena_AllocateFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||||
|
{
|
||||||
|
if (!block)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
DQN_ASSERTF((align & (align - 1)) == 0, "Power of two alignment required");
|
||||||
|
align = DQN_MAX(align, 1);
|
||||||
|
|
||||||
|
DQN_ASSERT(block->hwm_used <= block->commit);
|
||||||
|
DQN_ASSERT(block->hwm_used >= block->used);
|
||||||
|
DQN_ASSERTF(block->commit >= block->used,
|
||||||
|
"Internal error: Committed size must always be greater than the used size [commit=%_$$zd, used=%_$$zd]",
|
||||||
|
block->commit, block->used);
|
||||||
|
|
||||||
|
// NOTE: Calculate how much we need to pad the next pointer to divvy out
|
||||||
|
// (only if it is unaligned)
|
||||||
|
uintptr_t next_ptr = DQN_CAST(uintptr_t)block->memory + block->used;
|
||||||
|
Dqn_usize align_offset = 0;
|
||||||
|
if (next_ptr & (align - 1))
|
||||||
|
align_offset = (align - (next_ptr & (align - 1)));
|
||||||
|
|
||||||
|
Dqn_usize allocation_size = size + align_offset;
|
||||||
|
if ((block->used + allocation_size) > block->size)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
void *result = DQN_CAST(char *)next_ptr + align_offset;
|
||||||
|
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||||
|
// NOTE: Newly commit pages are always 0-ed out, we only need to
|
||||||
|
// memset the memory that are being reused.
|
||||||
|
Dqn_usize const reused_bytes = DQN_MIN(block->hwm_used - block->used, allocation_size);
|
||||||
|
DQN_MEMSET(DQN_CAST(void *)next_ptr, DQN_MEMSET_BYTE, reused_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Ensure requested bytes are backed by physical pages from the OS
|
||||||
|
Dqn_Arena_CommitFromBlock(block, allocation_size, Dqn_ArenaCommit_EnsureSpace);
|
||||||
|
|
||||||
|
// NOTE: Unlock the pages requested
|
||||||
|
if (block->arena->use_after_free_guard)
|
||||||
|
Dqn_VMem_Protect(result, allocation_size, Dqn_VMemPage_ReadWrite);
|
||||||
|
|
||||||
|
// NOTE: Update arena
|
||||||
|
Dqn_Arena *arena = block->arena;
|
||||||
|
arena->stats.used += allocation_size;
|
||||||
|
arena->stats.used_hwm = DQN_MAX(arena->stats.used_hwm, arena->stats.used);
|
||||||
|
block->used += allocation_size;
|
||||||
|
block->hwm_used = DQN_MAX(block->hwm_used, block->used);
|
||||||
|
|
||||||
|
DQN_ASSERTF(block->used <= block->commit, "Internal error: Committed size must be greater than used size [used=%_$$zd, commit=%_$$zd]", block->used, block->commit);
|
||||||
|
DQN_ASSERTF(block->commit <= block->size, "Internal error: Allocation exceeded block capacity [commit=%_$$zd, size=%_$$zd]", block->commit, block->size);
|
||||||
|
DQN_ASSERTF(((DQN_CAST(uintptr_t)result) & (align - 1)) == 0, "Internal error: Pointer alignment failed [address=%p, align=%x]", result, align);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_FILE_SCOPE void *Dqn_Arena_AllocatorAlloc_(DQN_LEAK_TRACE_FUNCTION size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context)
|
||||||
|
{
|
||||||
|
void *result = NULL;
|
||||||
|
if (!user_context)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
Dqn_Arena *arena = DQN_CAST(Dqn_Arena *)user_context;
|
||||||
|
result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size, align, zero_mem);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_FILE_SCOPE void Dqn_Arena_AllocatorDealloc_(DQN_LEAK_TRACE_FUNCTION void *ptr, size_t size, void *user_context)
|
||||||
|
{
|
||||||
|
// NOTE: No-op, arenas batch allocate and batch deallocate. Call free on the
|
||||||
|
// underlying arena, since we can't free individual pointers.
|
||||||
|
DQN_LEAK_TRACE_UNUSED;
|
||||||
|
(void)ptr;
|
||||||
|
(void)size;
|
||||||
|
(void)user_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_Allocator Dqn_Arena_Allocator(Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
Dqn_Allocator result = {};
|
||||||
|
if (arena) {
|
||||||
|
result.user_context = arena;
|
||||||
|
result.alloc = Dqn_Arena_AllocatorAlloc_;
|
||||||
|
result.dealloc = Dqn_Arena_AllocatorDealloc_;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dqn_ArenaBlockResetInfo_
|
||||||
|
{
|
||||||
|
bool free_memory;
|
||||||
|
Dqn_usize used_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the size in bytes required to allocate the memory for the block
|
||||||
|
// (*not* including the memory required for the user in the block!)
|
||||||
|
#define Dqn_Arena_BlockMetadataSize_(arena) ((arena)->use_after_free_guard ? (Dqn_PowerOfTwoAlign(sizeof(Dqn_ArenaBlock), DQN_VMEM_PAGE_GRANULARITY)) : sizeof(Dqn_ArenaBlock))
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaBlock *block, Dqn_ZeroMem zero_mem, Dqn_ArenaBlockResetInfo_ reset_info)
|
||||||
|
{
|
||||||
|
if (!block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (zero_mem == Dqn_ZeroMem_Yes)
|
||||||
|
DQN_MEMSET(block->memory, DQN_MEMSET_BYTE, block->commit);
|
||||||
|
|
||||||
|
if (reset_info.free_memory) {
|
||||||
|
Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(block->arena);
|
||||||
|
Dqn_VMem_Release(block, block_metadata_size + block->size);
|
||||||
|
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG block);
|
||||||
|
} else {
|
||||||
|
block->used = reset_info.used_value;
|
||||||
|
// NOTE: Guard all the committed pages again
|
||||||
|
if (block->arena->use_after_free_guard)
|
||||||
|
Dqn_VMem_Protect(block->memory, block->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||||
|
{
|
||||||
|
if (!arena)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// NOTE: Zero all the blocks until we reach the first block in the list
|
||||||
|
for (Dqn_ArenaBlock *block = arena->head; block; block = block->next) {
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE block, zero_mem, reset_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
arena->curr = arena->head;
|
||||||
|
arena->stats.used = 0;
|
||||||
|
arena->stats.wasted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
Dqn_ArenaTempMemory result = {};
|
||||||
|
if (arena) {
|
||||||
|
arena->temp_memory_count++;
|
||||||
|
result.arena = arena;
|
||||||
|
result.head = arena->head;
|
||||||
|
result.curr = arena->curr;
|
||||||
|
result.tail = arena->tail;
|
||||||
|
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||||
|
result.stats = arena->stats;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemory scope)
|
||||||
|
{
|
||||||
|
if (!scope.arena)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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.used = scope.stats.used;
|
||||||
|
arena->stats.wasted = scope.stats.wasted;
|
||||||
|
arena->stats.blocks = scope.stats.blocks;
|
||||||
|
|
||||||
|
// NOTE: Revert the current block to the scope's current block
|
||||||
|
arena->head = scope.head;
|
||||||
|
arena->curr = scope.curr;
|
||||||
|
if (arena->curr) {
|
||||||
|
Dqn_ArenaBlock *curr = arena->curr;
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
reset_info.used_value = scope.curr_used;
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG curr, Dqn_ZeroMem_No, reset_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Free the tail blocks until we reach the scope's tail block
|
||||||
|
while (arena->tail != scope.tail) {
|
||||||
|
Dqn_ArenaBlock *tail = arena->tail;
|
||||||
|
arena->tail = tail->prev;
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
reset_info.free_memory = true;
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG tail, Dqn_ZeroMem_No, reset_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Reset the usage of all the blocks between the tail and current block's
|
||||||
|
if (arena->tail) {
|
||||||
|
arena->tail->next = nullptr;
|
||||||
|
for (Dqn_ArenaBlock *block = arena->tail; block && block != arena->curr; block = block->prev) {
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, Dqn_ZeroMem_No, reset_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_ArenaTempMemoryScope_::Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
temp_memory = Dqn_Arena_BeginTempMemory(arena);
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
leak_site__ = DQN_LEAK_TRACE_ARG_NO_COMMA;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_ArenaTempMemoryScope_::~Dqn_ArenaTempMemoryScope_()
|
||||||
|
{
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
Dqn_Arena_EndTempMemory_(leak_site__, temp_memory);
|
||||||
|
#else
|
||||||
|
Dqn_Arena_EndTempMemory_(temp_memory);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_ArenaStatString Dqn_Arena_StatString(Dqn_ArenaStat const *stat)
|
||||||
|
{
|
||||||
|
// NOTE: We use a non-standard format string that is only usable via
|
||||||
|
// stb sprintf that GCC warns about as an error. This pragma mutes that.
|
||||||
|
#if defined(DQN_COMPILER_GCC)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat="
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-extra-args"
|
||||||
|
#elif defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat"
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-invalid-specifier"
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-extra-args"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Dqn_ArenaStatString result = {};
|
||||||
|
int size16 = STB_SPRINTF_DECORATE(snprintf)(result.data, DQN_ARRAY_ICOUNT(result.data),
|
||||||
|
"ArenaStat{"
|
||||||
|
"used/hwm=%_$$zd/%_$$zd, "
|
||||||
|
"cap/hwm=%_$$zd/%_$$zd, "
|
||||||
|
"wasted/hwm=%_$$zd/%_$$zd, "
|
||||||
|
"blocks/hwm=%_$$u/%_$$u, "
|
||||||
|
"syscalls=%'zd"
|
||||||
|
"}",
|
||||||
|
stat->used, stat->used_hwm,
|
||||||
|
stat->capacity, stat->capacity_hwm,
|
||||||
|
stat->wasted, stat->wasted_hwm,
|
||||||
|
stat->blocks, stat->blocks_hwm,
|
||||||
|
stat->syscalls);
|
||||||
|
result.size = Dqn_Safe_SaturateCastIntToU16(size16);
|
||||||
|
|
||||||
|
#if defined(DQN_COMPILER_GCC)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#elif defined(DQN_COMPILER_W32_CLANG)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_LogStats(Dqn_Arena const *arena)
|
||||||
|
{
|
||||||
|
Dqn_ArenaStatString string = Dqn_Arena_StatString(&arena->stats);
|
||||||
|
Dqn_Log_InfoF("%.*s\n", DQN_STRING_FMT(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_ArenaBlock *Dqn_Arena_Grow_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(commit <= size);
|
||||||
|
if (!arena || size == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(arena);
|
||||||
|
commit = DQN_MIN(commit, size);
|
||||||
|
Dqn_usize reserve_aligned = Dqn_PowerOfTwoAlign(size + block_metadata_size, DQN_VMEM_RESERVE_GRANULARITY);
|
||||||
|
Dqn_usize commit_aligned = Dqn_PowerOfTwoAlign(commit + block_metadata_size, DQN_VMEM_COMMIT_GRANULARITY);
|
||||||
|
DQN_ASSERT(commit_aligned < reserve_aligned);
|
||||||
|
|
||||||
|
// NOTE: If the commit amount is the same as reserve size we can save one
|
||||||
|
// syscall by asking the OS to reserve+commit in the same call.
|
||||||
|
Dqn_VMemCommit commit_on_reserve = reserve_aligned == commit_aligned ? Dqn_VMemCommit_Yes : Dqn_VMemCommit_No;
|
||||||
|
uint32_t page_flags = Dqn_VMemPage_ReadWrite;
|
||||||
|
auto *result = DQN_CAST(Dqn_ArenaBlock *)Dqn_VMem_Reserve(reserve_aligned, commit_on_reserve, page_flags);
|
||||||
|
if (result) {
|
||||||
|
// NOTE: Commit the amount requested by the user if we did not commit
|
||||||
|
// on reserve the initial range.
|
||||||
|
if (commit_on_reserve == Dqn_VMemCommit_No) {
|
||||||
|
Dqn_VMem_Commit(result, commit_aligned, page_flags);
|
||||||
|
} else {
|
||||||
|
DQN_ASSERT(commit_aligned == reserve_aligned);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Sanity check memory is zero-ed out
|
||||||
|
DQN_ASSERT(result->used == 0);
|
||||||
|
DQN_ASSERT(result->next == nullptr);
|
||||||
|
DQN_ASSERT(result->prev == nullptr);
|
||||||
|
|
||||||
|
// NOTE: Setup the block
|
||||||
|
result->memory = DQN_CAST(uint8_t *)result + block_metadata_size;
|
||||||
|
result->size = reserve_aligned - block_metadata_size;
|
||||||
|
result->commit = commit_aligned - block_metadata_size;
|
||||||
|
result->flags = flags;
|
||||||
|
result->arena = arena;
|
||||||
|
|
||||||
|
// NOTE: Reset the block (this will guard the memory pages if required, otherwise no-op).
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG result, Dqn_ZeroMem_No, reset_info);
|
||||||
|
|
||||||
|
// NOTE: Attach the block to the arena
|
||||||
|
if (arena->tail) {
|
||||||
|
arena->tail->next = result;
|
||||||
|
result->prev = arena->tail;
|
||||||
|
} else {
|
||||||
|
DQN_ASSERT(!arena->curr);
|
||||||
|
arena->curr = result;
|
||||||
|
arena->head = result;
|
||||||
|
}
|
||||||
|
arena->tail = result;
|
||||||
|
|
||||||
|
// NOTE: Update stats
|
||||||
|
arena->stats.syscalls += (commit_on_reserve ? 1 : 2);
|
||||||
|
arena->stats.blocks += 1;
|
||||||
|
arena->stats.capacity += arena->curr->size;
|
||||||
|
|
||||||
|
arena->stats.blocks_hwm = DQN_MAX(arena->stats.blocks_hwm, arena->stats.blocks);
|
||||||
|
arena->stats.capacity_hwm = DQN_MAX(arena->stats.capacity_hwm, arena->stats.capacity);
|
||||||
|
|
||||||
|
Dqn_Library_LeakTraceAdd(DQN_LEAK_TRACE_ARG result, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void *Dqn_Arena_Allocate_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||||
|
{
|
||||||
|
DQN_ASSERTF((align & (align - 1)) == 0, "Power of two alignment required");
|
||||||
|
align = DQN_MAX(align, 1);
|
||||||
|
|
||||||
|
if (arena->use_after_free_guard) {
|
||||||
|
size = Dqn_PowerOfTwoAlign(size, DQN_VMEM_PAGE_GRANULARITY);
|
||||||
|
align = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *result = nullptr;
|
||||||
|
for (; !result;) {
|
||||||
|
while (arena->curr && (arena->curr->flags & Dqn_ArenaBlockFlags_Private))
|
||||||
|
arena->curr = arena->curr->next;
|
||||||
|
|
||||||
|
result = Dqn_Arena_AllocateFromBlock(arena->curr, size, align, zero_mem);
|
||||||
|
if (!result) {
|
||||||
|
if (!arena->curr || arena->curr == arena->tail) {
|
||||||
|
Dqn_usize allocation_size = size + (align - 1);
|
||||||
|
if (!Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG arena, allocation_size, allocation_size /*commit*/, 0 /*flags*/)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arena->curr != arena->tail)
|
||||||
|
arena->curr = arena->curr->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
DQN_ASSERT((arena->curr->flags & Dqn_ArenaBlockFlags_Private) == 0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void *Dqn_Arena_Copy_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||||
|
{
|
||||||
|
void *result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size, align, Dqn_ZeroMem_No);
|
||||||
|
DQN_MEMCPY(result, src, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void *Dqn_Arena_CopyZ_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||||
|
{
|
||||||
|
void *result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size + 1, align, Dqn_ZeroMem_No);
|
||||||
|
DQN_MEMCPY(result, src, size);
|
||||||
|
(DQN_CAST(char *)result)[size] = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_Free_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||||
|
{
|
||||||
|
if (!arena)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!DQN_CHECKF(arena->temp_memory_count == 0, "You cannot free an arena whilst in an temp memory region"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (arena->tail) {
|
||||||
|
Dqn_ArenaBlock *block = arena->tail;
|
||||||
|
arena->tail = block->prev;
|
||||||
|
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||||
|
reset_info.free_memory = true;
|
||||||
|
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, zero_mem, reset_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
arena->curr = arena->tail = nullptr;
|
||||||
|
arena->stats.capacity = 0;
|
||||||
|
arena->stats.used = 0;
|
||||||
|
arena->stats.wasted = 0;
|
||||||
|
arena->stats.blocks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
catalog->arena = arena;
|
||||||
|
catalog->sentinel.next = &catalog->sentinel;
|
||||||
|
catalog->sentinel.prev = &catalog->sentinel;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_ArenaCatalog_Add(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
// NOTE: We could use an atomic for appending to the sentinel but it is such
|
||||||
|
// a rare operation to append to the catalog that we don't bother.
|
||||||
|
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||||
|
|
||||||
|
// NOTE: Create item in the catalog
|
||||||
|
Dqn_ArenaCatalogItem *result = Dqn_Arena_New(catalog->arena, Dqn_ArenaCatalogItem, Dqn_ZeroMem_Yes);
|
||||||
|
result->arena = arena;
|
||||||
|
|
||||||
|
// NOTE: Add to the catalog (linked list)
|
||||||
|
Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel;
|
||||||
|
result->next = sentinel;
|
||||||
|
result->prev = sentinel->prev;
|
||||||
|
result->next->prev = result;
|
||||||
|
result->prev->next = result;
|
||||||
|
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||||
|
|
||||||
|
Dqn_Atomic_AddU32(&catalog->arena_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit)
|
||||||
|
{
|
||||||
|
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||||
|
Dqn_Arena *result = Dqn_Arena_New(catalog->arena, Dqn_Arena, Dqn_ZeroMem_Yes);
|
||||||
|
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||||
|
|
||||||
|
Dqn_Arena_Grow(result, byte_size, commit, 0 /*flags*/);
|
||||||
|
Dqn_ArenaCatalog_Add(catalog, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_Arena *result = Dqn_ArenaCatalog_Alloc(catalog, byte_size, commit);
|
||||||
|
result->label = Dqn_String8_InitFV(Dqn_Arena_Allocator(result), fmt, args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Arena *result = Dqn_ArenaCatalog_AllocFV(catalog, byte_size, commit, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
339
dqn_memory.h
Normal file
339
dqn_memory.h
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$ALLO] Dqn_Allocator | | Generic allocator interface
|
||||||
|
// =================================================================================================
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
#if defined(DQN_NO_DSMAP)
|
||||||
|
#error "DSMap is required for allocation tracing"
|
||||||
|
#endif
|
||||||
|
#define DQN_LEAK_TRACE DQN_CALL_SITE,
|
||||||
|
#define DQN_LEAK_TRACE_NO_COMMA DQN_CALL_SITE
|
||||||
|
#define DQN_LEAK_TRACE_FUNCTION Dqn_CallSite leak_site_,
|
||||||
|
#define DQN_LEAK_TRACE_FUNCTION_NO_COMMA Dqn_CallSite leak_site_
|
||||||
|
#define DQN_LEAK_TRACE_ARG leak_site_,
|
||||||
|
#define DQN_LEAK_TRACE_ARG_NO_COMMA leak_site_
|
||||||
|
#define DQN_LEAK_TRACE_UNUSED (void)leak_site_;
|
||||||
|
#else
|
||||||
|
#define DQN_LEAK_TRACE
|
||||||
|
#define DQN_LEAK_TRACE_NO_COMMA
|
||||||
|
#define DQN_LEAK_TRACE_FUNCTION
|
||||||
|
#define DQN_LEAK_TRACE_FUNCTION_NO_COMMA
|
||||||
|
#define DQN_LEAK_TRACE_ARG
|
||||||
|
#define DQN_LEAK_TRACE_ARG_NO_COMMA
|
||||||
|
#define DQN_LEAK_TRACE_UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct Dqn_LeakTrace {
|
||||||
|
void *ptr; ///< The pointer we are tracking
|
||||||
|
Dqn_usize size; ///< Size of the allocation
|
||||||
|
Dqn_CallSite call_site; ///< Call site where the allocation was allocated
|
||||||
|
bool freed; ///< True if this pointer has been freed
|
||||||
|
Dqn_usize freed_size; ///< Size of the allocation that has been freed
|
||||||
|
Dqn_CallSite freed_call_site; ///< Call site where the allocation was freed
|
||||||
|
} Dqn_LeakTrace;
|
||||||
|
|
||||||
|
/// Allocate memory
|
||||||
|
/// @param size The number of bytes to allocate
|
||||||
|
/// @param zero_mem Flag to indicate if the allocated memory should be zero-ed out
|
||||||
|
/// @param user_context The user assigned pointer in the allocator
|
||||||
|
typedef void *Dqn_Allocator_AllocProc(DQN_LEAK_TRACE_FUNCTION size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context);
|
||||||
|
|
||||||
|
/// Deallocate memory
|
||||||
|
/// @param ptr The pointer to deallocate memory for
|
||||||
|
/// @param size The number of bytes to deallocate
|
||||||
|
/// @param user_context The user assigned pointer in the allocator
|
||||||
|
typedef void Dqn_Allocator_DeallocProc(DQN_LEAK_TRACE_FUNCTION void *ptr, size_t size, void *user_context);
|
||||||
|
|
||||||
|
typedef struct Dqn_Allocator {
|
||||||
|
void *user_context; ///< User assigned pointer that is passed into the allocator functions
|
||||||
|
Dqn_Allocator_AllocProc *alloc; ///< Memory allocating routine
|
||||||
|
Dqn_Allocator_DeallocProc *dealloc; ///< Memory deallocating routine
|
||||||
|
} Dqn_Allocator;
|
||||||
|
|
||||||
|
/// Allocate bytes from the given allocator
|
||||||
|
/// @param[in] allocator The allocator to allocate bytes from
|
||||||
|
/// @param[in] size The amount of bytes to allocator
|
||||||
|
/// @param[in] zero_mem Flag to indicate if the allocated must be zero-ed out
|
||||||
|
#define Dqn_Allocator_Alloc(allocator, size, align, zero_mem) Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, size, align, zero_mem)
|
||||||
|
void *Dqn_Allocator_Alloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||||
|
|
||||||
|
/// Deallocate the memory from the pointer using the allocator
|
||||||
|
///
|
||||||
|
/// The pointer must originally have been allocated from the passed in
|
||||||
|
/// allocator. The size must also match the size that was originally allocated.
|
||||||
|
///
|
||||||
|
/// @param[in] allocator The allocator to allocate bytes from
|
||||||
|
/// @param[in] ptr The pointer to deallocate
|
||||||
|
/// @param[in] size The amount of bytes to deallocate.
|
||||||
|
#define Dqn_Allocator_Dealloc(allocator, ptr, size) Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE allocator, ptr, size)
|
||||||
|
void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, void *ptr, size_t size);
|
||||||
|
|
||||||
|
#define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||||
|
#define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type), alignof(Type), zero_mem)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$VMEM] Dqn_VMem | | Virtual memory allocation
|
||||||
|
// =================================================================================================
|
||||||
|
enum Dqn_VMemCommit
|
||||||
|
{
|
||||||
|
Dqn_VMemCommit_No,
|
||||||
|
Dqn_VMemCommit_Yes,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_VMemPage
|
||||||
|
{
|
||||||
|
/// Exception on read/write with a page. This flag overrides the read/write access.
|
||||||
|
Dqn_VMemPage_NoAccess = 1 << 0,
|
||||||
|
|
||||||
|
/// Only read permitted on the page.
|
||||||
|
Dqn_VMemPage_Read = 1 << 1,
|
||||||
|
|
||||||
|
/// Only write permitted on the page. On Windows this is not supported and will be promoted to
|
||||||
|
/// read+write permissions.
|
||||||
|
Dqn_VMemPage_Write = 1 << 2,
|
||||||
|
|
||||||
|
Dqn_VMemPage_ReadWrite = Dqn_VMemPage_Read | Dqn_VMemPage_Write,
|
||||||
|
|
||||||
|
/// Modifier used in conjunction with previous flags. Raises exception on first access to the
|
||||||
|
/// page, then, the underlying protection flags are active. This is supported on Windows, on
|
||||||
|
/// other OS's using this flag will set the OS equivalent of Dqn_VMemPage_NoAccess.
|
||||||
|
Dqn_VMemPage_Guard = 1 << 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(DQN_VMEM_PAGE_GRANULARITY)
|
||||||
|
#define DQN_VMEM_PAGE_GRANULARITY DQN_KILOBYTES(4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_VMEM_RESERVE_GRANULARITY)
|
||||||
|
#define DQN_VMEM_RESERVE_GRANULARITY DQN_KILOBYTES(64)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DQN_VMEM_COMMIT_GRANULARITY)
|
||||||
|
#define DQN_VMEM_COMMIT_GRANULARITY DQN_VMEM_PAGE_GRANULARITY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(Dqn_IsPowerOfTwo(DQN_VMEM_RESERVE_GRANULARITY),
|
||||||
|
"This library assumes that the memory allocation routines from the OS has PoT allocation granularity");
|
||||||
|
static_assert(Dqn_IsPowerOfTwo(DQN_VMEM_COMMIT_GRANULARITY),
|
||||||
|
"This library assumes that the memory allocation routines from the OS has PoT allocation granularity");
|
||||||
|
static_assert(DQN_VMEM_COMMIT_GRANULARITY < DQN_VMEM_RESERVE_GRANULARITY,
|
||||||
|
"Minimum commit size must be lower than the reserve size to avoid OOB math on pointers in this library");
|
||||||
|
|
||||||
|
DQN_API void *Dqn_VMem_Reserve (Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags);
|
||||||
|
DQN_API bool Dqn_VMem_Commit (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||||
|
DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size);
|
||||||
|
DQN_API void Dqn_VMem_Release (void *ptr, Dqn_usize size);
|
||||||
|
DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$AREN] Dqn_Arena | | Growing bump allocator
|
||||||
|
// =================================================================================================
|
||||||
|
//
|
||||||
|
// A bump-allocator that can grow dynamically by chaining blocks of memory
|
||||||
|
// together. The arena's memory is backed by virtual memory allowing the
|
||||||
|
// allocator to reserve and commit physical pages as memory is given from
|
||||||
|
// the block of memory.
|
||||||
|
//
|
||||||
|
// Arena's allow grouping of multiple allocations into one lifetime that is
|
||||||
|
// bound to the arena. Allocation involves a simple 'bump' of the pointer in the
|
||||||
|
// memory block. Freeing involves resetting the pointer to the start of the
|
||||||
|
// block and/or releasing the single pointer to the entire block of memory.
|
||||||
|
//
|
||||||
|
// This allocator reserves memory blocks at a 64k granularity as per the minimum
|
||||||
|
// granularity reserve size of VirtualAlloc on Windows. Memory is commit at
|
||||||
|
// a 4k granularity for similar reasons. On 64 bit platforms you have access
|
||||||
|
// to 48 bits of address space for applications, this is 256TB of address space
|
||||||
|
// you can reserve. The typical usage for this style of arena is to reserve
|
||||||
|
// as much space as you possibly need, ever, for the lifetime of the arena (e.g.
|
||||||
|
// 64GB) since the arena only commits as much as needed.
|
||||||
|
//
|
||||||
|
// NOTE: API
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_Grow
|
||||||
|
// @desc Grow the arena's capacity by allocating a block of memory with the
|
||||||
|
// requested size. The requested size is rounded up to the nearest 64k
|
||||||
|
// boundary as that is the minimum reserve granularity (atleast on Windows)
|
||||||
|
// for virtual memory.
|
||||||
|
// @param size[in] The size in bytes to expand the capacity of the arena
|
||||||
|
// @param commit[in] The amount of bytes to request to be physically backed by
|
||||||
|
// pages from the OS.
|
||||||
|
// @param flags[in] Bit flags from 'Dqn_ArenaBlockFlags', set to 0 if none
|
||||||
|
// @return The block of memory that
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_Allocate, Dqn_Arena_New, Dqn_Arena_NewArray,
|
||||||
|
// Dqn_Arena_NewArrayWithBlock,
|
||||||
|
// @desc Allocate byte/objects
|
||||||
|
// `Allocate` allocates bytes
|
||||||
|
// `New` allocates an object
|
||||||
|
// `NewArray` allocates an array of objects
|
||||||
|
// `NewArrayWithBlock` allocates an array of objects from the given memory 'block'
|
||||||
|
// @return A pointer to the allocated bytes/object. Null pointer on failure
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_Copy, Dqn_Arena_CopyZ
|
||||||
|
// @desc Allocate a copy of an object's bytes. The 'Z' variant adds
|
||||||
|
// a null-terminating byte at the end of the stream.
|
||||||
|
// @return A pointer to the allocated object. Null pointer on failure.
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_Reset
|
||||||
|
// @desc Set the arena's current block to the first block in the linked list
|
||||||
|
// of blocks and mark all blocks free.
|
||||||
|
// @param[in] zero_mem When yes, the memory is cleared using DQN_MEMSET with the
|
||||||
|
// value of DQN_MEMSET_BYTE
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_Free
|
||||||
|
// @desc Free the arena returning all memory back to the OS
|
||||||
|
// @param[in] zero_mem: When true, the memory is cleared using DQN_MEMSET with
|
||||||
|
// the value of DQN_MEMSET_BYTE
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_BeginTempMemory
|
||||||
|
// @desc Begin an allocation scope where all allocations between begin and end
|
||||||
|
// calls will be reverted. Useful for short-lived or highly defined lifetime
|
||||||
|
// allocations. An allocation scope is invalidated if the arena is freed
|
||||||
|
// between the begin and end call.
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_EndTempMemory
|
||||||
|
// @desc End an allocation scope previously begun by calling begin scope.
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_StatString
|
||||||
|
// @desc Dump the stats of the given arena to a string
|
||||||
|
// @param[in] arena The arena to dump stats for
|
||||||
|
// @return A stack allocated string containing the stats of the arena
|
||||||
|
//
|
||||||
|
// @proc Dqn_Arena_LogStats
|
||||||
|
// @desc Dump the stats of the given arena to the memory log-stream.
|
||||||
|
// @param[in] arena The arena to dump stats for
|
||||||
|
|
||||||
|
enum Dqn_ArenaBlockFlags
|
||||||
|
{
|
||||||
|
Dqn_ArenaBlockFlags_Private = 1 << 0, ///< Private blocks can only allocate its memory when used in the 'FromBlock' API variants
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ArenaStat
|
||||||
|
{
|
||||||
|
Dqn_usize capacity; ///< Total allocating capacity of the arena in bytes
|
||||||
|
Dqn_usize used; ///< Total amount of bytes used in the arena
|
||||||
|
Dqn_usize wasted; ///< Orphaned space in blocks due to allocations requiring more space than available in the active block
|
||||||
|
uint32_t blocks; ///< Number of memory blocks in the arena
|
||||||
|
Dqn_usize syscalls; ///< Number of memory allocation syscalls into the OS
|
||||||
|
|
||||||
|
Dqn_usize capacity_hwm; ///< High-water mark for 'capacity'
|
||||||
|
Dqn_usize used_hwm; ///< High-water mark for 'used'
|
||||||
|
Dqn_usize wasted_hwm; ///< High-water mark for 'wasted'
|
||||||
|
uint32_t blocks_hwm; ///< High-water mark for 'blocks'
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ArenaBlock
|
||||||
|
{
|
||||||
|
struct Dqn_Arena *arena; ///< Arena that owns this block
|
||||||
|
void *memory; ///< Backing memory of the block
|
||||||
|
Dqn_usize size; ///< Size of the block
|
||||||
|
Dqn_usize used; ///< Number of bytes used up in the block. Always less than the commit amount.
|
||||||
|
Dqn_usize hwm_used;///< High-water mark for 'used' bytes in this block
|
||||||
|
Dqn_usize commit; ///< Number of bytes in the block physically backed by pages
|
||||||
|
Dqn_ArenaBlock *prev; ///< Previous linked block
|
||||||
|
Dqn_ArenaBlock *next; ///< Next linked block
|
||||||
|
uint8_t flags; ///< Bit field for 'Dqn_ArenaBlockFlags'
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ArenaStatString
|
||||||
|
{
|
||||||
|
char data[256];
|
||||||
|
uint16_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_Arena
|
||||||
|
{
|
||||||
|
bool use_after_free_guard;
|
||||||
|
uint32_t temp_memory_count;
|
||||||
|
Dqn_String8 label; ///< Optional label to describe the arena
|
||||||
|
Dqn_ArenaBlock *head; ///< Active block the arena is allocating from
|
||||||
|
Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
|
||||||
|
Dqn_ArenaBlock *tail; ///< Last block in the linked list of blocks
|
||||||
|
Dqn_ArenaStat stats; ///< Current arena stats, reset when reset usage is invoked.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ArenaTempMemory
|
||||||
|
{
|
||||||
|
Dqn_Arena *arena; ///< Arena the scope is for
|
||||||
|
Dqn_ArenaBlock *head; ///< Head block of the arena at the beginning of the scope
|
||||||
|
Dqn_ArenaBlock *curr; ///< Current block of the arena at the beginning of the scope
|
||||||
|
Dqn_ArenaBlock *tail; ///< Tail block of the arena at the beginning of the scope
|
||||||
|
Dqn_usize curr_used; ///< Current used amount of the current block
|
||||||
|
Dqn_ArenaStat stats; ///< Stats of the arena at the beginning of the scope
|
||||||
|
};
|
||||||
|
|
||||||
|
// Automatically begin and end a temporary memory scope on object construction
|
||||||
|
// and destruction respectively.
|
||||||
|
#define DQN_ARENA_TEMP_MEMORY_SCOPE(arena) Dqn_ArenaTempMemoryScope_ DQN_UNIQUE_NAME(temp_memory_) = Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE arena)
|
||||||
|
#define Dqn_ArenaTempMemoryScope(arena) Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE arena)
|
||||||
|
struct Dqn_ArenaTempMemoryScope_
|
||||||
|
{
|
||||||
|
Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena);
|
||||||
|
~Dqn_ArenaTempMemoryScope_();
|
||||||
|
Dqn_ArenaTempMemory temp_memory;
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
Dqn_CallSite leak_site__;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_ArenaCommit
|
||||||
|
{
|
||||||
|
/// Commit the pages to ensure the block has the requested commit amount.
|
||||||
|
/// No-op if the block has sufficient commit space already.
|
||||||
|
Dqn_ArenaCommit_EnsureSpace,
|
||||||
|
Dqn_ArenaCommit_GetNewPages, ///< Grow the block by the requested commit amount
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: Allocation ================================================================================
|
||||||
|
#define Dqn_Arena_Grow(arena, size, commit, flags) Dqn_Arena_Grow_(DQN_LEAK_TRACE arena, size, commit, flags)
|
||||||
|
#define Dqn_Arena_Allocate(arena, size, align, zero_mem) Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, size, align, zero_mem)
|
||||||
|
#define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, sizeof(Type), alignof(Type), zero_mem)
|
||||||
|
#define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||||
|
#define Dqn_Arena_NewArrayWithBlock(block, Type, count, zero_mem) (Type *)Dqn_Arena_AllocateFromBlock(block, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||||
|
#define Dqn_Arena_Copy(arena, Type, src, count) (Type *)Dqn_Arena_Copy_(DQN_LEAK_TRACE arena, src, sizeof(*src) * count, alignof(Type))
|
||||||
|
#define Dqn_Arena_CopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ_(DQN_LEAK_TRACE arena, src, sizeof(*src) * count, alignof(Type))
|
||||||
|
#define Dqn_Arena_Free(arena, zero_mem) Dqn_Arena_Free_(DQN_LEAK_TRACE arena, zero_mem)
|
||||||
|
|
||||||
|
DQN_API void Dqn_Arena_CommitFromBlock (Dqn_ArenaBlock *block, Dqn_usize size, Dqn_ArenaCommit commit);
|
||||||
|
DQN_API void * Dqn_Arena_AllocateFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||||
|
DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena);
|
||||||
|
DQN_API void Dqn_Arena_Reset (Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||||
|
|
||||||
|
// NOTE: Temp Memory ===============================================================================
|
||||||
|
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory (Dqn_Arena *arena);
|
||||||
|
#define Dqn_Arena_EndTempMemory(arena_temp_memory) Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE arena_temp_memory)
|
||||||
|
|
||||||
|
// NOTE: Arena Stats ===============================================================================
|
||||||
|
DQN_API Dqn_ArenaStatString Dqn_Arena_StatString (Dqn_ArenaStat const *stat);
|
||||||
|
DQN_API void Dqn_Arena_LogStats (Dqn_Arena const *arena);
|
||||||
|
|
||||||
|
// NOTE: Internal ==================================================================================
|
||||||
|
DQN_API Dqn_ArenaBlock * Dqn_Arena_Grow_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags);
|
||||||
|
DQN_API void * Dqn_Arena_Allocate_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||||
|
DQN_API void * Dqn_Arena_Copy_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||||
|
DQN_API void Dqn_Arena_Free_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||||
|
DQN_API void * Dqn_Arena_CopyZ_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||||
|
DQN_API void Dqn_Arena_EndTempMemory_ (DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemory arena_temp_memory);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
|
||||||
|
// =================================================================================================
|
||||||
|
struct Dqn_ArenaCatalogItem
|
||||||
|
{
|
||||||
|
Dqn_Arena *arena;
|
||||||
|
Dqn_ArenaCatalogItem *next;
|
||||||
|
Dqn_ArenaCatalogItem *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ArenaCatalog
|
||||||
|
{
|
||||||
|
Dqn_TicketMutex ticket_mutex; ///< Mutex for adding to the linked list of arenas
|
||||||
|
Dqn_Arena *arena;
|
||||||
|
Dqn_ArenaCatalogItem sentinel;
|
||||||
|
uint16_t arena_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena);
|
||||||
|
DQN_API void Dqn_ArenaCatalog_Add (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena);
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit);
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, va_list args);
|
||||||
|
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, char const *fmt, ...);
|
596
dqn_misc.cpp
Normal file
596
dqn_misc.cpp
Normal file
@ -0,0 +1,596 @@
|
|||||||
|
Dqn_Library dqn_library;
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$DLIB] Dqn_Library | | Library run-time behaviour configuration
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API Dqn_Library *Dqn_Library_Init(Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
Dqn_Library *result = &dqn_library;
|
||||||
|
Dqn_TicketMutex_Begin(&result->lib_mutex);
|
||||||
|
if (!result->lib_init) {
|
||||||
|
Dqn_ArenaCatalog_Init(&result->arena_catalog, arena ? arena : &result->arena_catalog_backup_arena);
|
||||||
|
result->lib_init = true;
|
||||||
|
}
|
||||||
|
Dqn_TicketMutex_End(&result->lib_mutex);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data)
|
||||||
|
{
|
||||||
|
dqn_library.log_callback = proc;
|
||||||
|
dqn_library.log_user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Library_SetLogFile(FILE *file)
|
||||||
|
{
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||||
|
dqn_library.log_file = file;
|
||||||
|
dqn_library.log_to_file = file ? true : false;
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path)
|
||||||
|
{
|
||||||
|
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||||
|
// NOTE: Open a file to write the arena stats to
|
||||||
|
FILE *file = nullptr;
|
||||||
|
fopen_s(&file, file_path.data, "a+b");
|
||||||
|
if (file) {
|
||||||
|
Dqn_Log_ErrorF("Failed to dump thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Copy the stats from library book-keeping
|
||||||
|
// NOTE: Extremely short critical section, copy the stats then do our
|
||||||
|
// work on it.
|
||||||
|
Dqn_ArenaStat stats[Dqn_CArray_CountI(dqn_library.thread_context_arena_stats)];
|
||||||
|
int stats_size = 0;
|
||||||
|
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.thread_context_mutex);
|
||||||
|
stats_size = dqn_library.thread_context_arena_stats_count;
|
||||||
|
DQN_MEMCPY(stats, dqn_library.thread_context_arena_stats, sizeof(stats[0]) * stats_size);
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.thread_context_mutex);
|
||||||
|
|
||||||
|
// NOTE: Print the cumulative stat
|
||||||
|
Dqn_DateHMSTimeString now = Dqn_Date_HMSLocalTimeStringNow();
|
||||||
|
fprintf(file,
|
||||||
|
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||||
|
now.date_size, now.date,
|
||||||
|
now.hms_size, now.hms,
|
||||||
|
dqn_library.thread_context_arena_stats_count);
|
||||||
|
|
||||||
|
// NOTE: Write the cumulative thread arena data
|
||||||
|
{
|
||||||
|
Dqn_ArenaStat stat = {};
|
||||||
|
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||||
|
Dqn_ArenaStat const *current = stats + index;
|
||||||
|
stat.capacity += current->capacity;
|
||||||
|
stat.used += current->used;
|
||||||
|
stat.wasted += current->wasted;
|
||||||
|
stat.blocks += current->blocks;
|
||||||
|
|
||||||
|
stat.capacity_hwm = DQN_MAX(stat.capacity_hwm, current->capacity_hwm);
|
||||||
|
stat.used_hwm = DQN_MAX(stat.used_hwm, current->used_hwm);
|
||||||
|
stat.wasted_hwm = DQN_MAX(stat.wasted_hwm, current->wasted_hwm);
|
||||||
|
stat.blocks_hwm = DQN_MAX(stat.blocks_hwm, current->blocks_hwm);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_ArenaStatString stats_string = Dqn_Arena_StatString(&stat);
|
||||||
|
fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Print individual thread arena data
|
||||||
|
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||||
|
Dqn_ArenaStat const *current = stats + index;
|
||||||
|
Dqn_ArenaStatString current_string = Dqn_Arena_StatString(current);
|
||||||
|
fprintf(file, " [%03d] CURR %.*s\n", DQN_CAST(int)index, current_string.size, current_string.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
Dqn_Log_InfoF("Dumped thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||||
|
#else
|
||||||
|
(void)file_path;
|
||||||
|
#endif // #if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
DQN_API void Dqn_Library_LeakTraceAdd(Dqn_CallSite call_site, void *ptr, Dqn_usize size)
|
||||||
|
{
|
||||||
|
if (!ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.alloc_table_mutex);
|
||||||
|
if (!Dqn_DSMap_IsValid(&dqn_library.alloc_table))
|
||||||
|
dqn_library.alloc_table = Dqn_DSMap_Init<Dqn_LeakTrace>(4096);
|
||||||
|
|
||||||
|
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
|
||||||
|
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it already existed.
|
||||||
|
Dqn_LeakTrace *trace = Dqn_DSMap_Find(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||||
|
if (trace) {
|
||||||
|
DQN_HARD_ASSERTF(trace->freed, "This pointer is already in the leak tracker, however it"
|
||||||
|
" has not been freed yet. Somehow this pointer has been"
|
||||||
|
" given to the allocation table and has not being marked"
|
||||||
|
" freed with an equivalent call to LeakTraceMarkFree()"
|
||||||
|
" [ptr=%p, size=%_$$d, file=\"%.*s:%u\","
|
||||||
|
" function=\"%.*s\"]",
|
||||||
|
ptr,
|
||||||
|
size,
|
||||||
|
DQN_STRING_FMT(trace->call_site.file),
|
||||||
|
trace->call_site.line,
|
||||||
|
DQN_STRING_FMT(trace->call_site.function));
|
||||||
|
} else {
|
||||||
|
trace = Dqn_DSMap_Make(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace->ptr = ptr;
|
||||||
|
trace->size = size;
|
||||||
|
trace->call_site = call_site;
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.alloc_table_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Library_LeakTraceMarkFree(Dqn_CallSite call_site, void *ptr)
|
||||||
|
{
|
||||||
|
if (!ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.alloc_table_mutex);
|
||||||
|
|
||||||
|
Dqn_LeakTrace *trace = Dqn_DSMap_Find(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||||
|
DQN_HARD_ASSERTF(trace, "Allocated pointer can not be removed as it does not exist in the"
|
||||||
|
" allocation table. When this memory was allocated, the pointer was"
|
||||||
|
" not added to the allocation table [ptr=%p]",
|
||||||
|
ptr);
|
||||||
|
|
||||||
|
DQN_HARD_ASSERTF(!trace->freed,
|
||||||
|
"Double free detected, pointer was previously allocated at [ptr=%p, %_$$d, file=\"%.*s:%u\", function=\"%.*s\"]",
|
||||||
|
ptr,
|
||||||
|
trace->size,
|
||||||
|
DQN_STRING_FMT(trace->call_site.file),
|
||||||
|
trace->call_site.line,
|
||||||
|
DQN_STRING_FMT(trace->call_site.function));
|
||||||
|
|
||||||
|
trace->freed = true;
|
||||||
|
trace->freed_size = trace->size;
|
||||||
|
trace->freed_call_site = call_site;
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.alloc_table_mutex);
|
||||||
|
}
|
||||||
|
#endif /// defined(DQN_LEAK_TRACING)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$BITS] Dqn_Bit | | Bitset manipulation
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield)
|
||||||
|
{
|
||||||
|
*flags = (*flags & ~bitfield);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Bit_SetInplace(uint64_t *flags, uint64_t bitfield)
|
||||||
|
{
|
||||||
|
*flags = (*flags | bitfield);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Bit_IsSet(uint64_t bits, uint64_t bits_to_set)
|
||||||
|
{
|
||||||
|
auto result = DQN_CAST(bool)((bits & bits_to_set) == bits_to_set);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check)
|
||||||
|
{
|
||||||
|
auto result = !Dqn_Bit_IsSet(bits, bits_to_check);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
||||||
|
{
|
||||||
|
int64_t result = DQN_CHECKF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b)
|
||||||
|
{
|
||||||
|
int64_t result = DQN_CHECKF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
uint64_t result = DQN_CHECKF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
uint64_t result = DQN_CHECKF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
uint64_t result = DQN_CHECKF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
||||||
|
{
|
||||||
|
uint32_t result = DQN_CHECKF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastUSizeToI*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// INT*_MAX literals will be promoted to the type of uintmax_t as uintmax_t is
|
||||||
|
// the highest possible rank (unsigned > signed).
|
||||||
|
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val)
|
||||||
|
{
|
||||||
|
int result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val)
|
||||||
|
{
|
||||||
|
int8_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT8_MAX) ? DQN_CAST(int8_t)val : INT8_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val)
|
||||||
|
{
|
||||||
|
int16_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT16_MAX) ? DQN_CAST(int16_t)val : INT16_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val)
|
||||||
|
{
|
||||||
|
int32_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT32_MAX) ? DQN_CAST(int32_t)val : INT32_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
||||||
|
{
|
||||||
|
int64_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT64_MAX) ? DQN_CAST(int64_t)val : INT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastUSizeToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||||
|
// match the highest rank operand.
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val)
|
||||||
|
{
|
||||||
|
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val)
|
||||||
|
{
|
||||||
|
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val)
|
||||||
|
{
|
||||||
|
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
||||||
|
{
|
||||||
|
uint64_t result = DQN_CHECK(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastU64ToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||||
|
// match the highest rank operand.
|
||||||
|
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
||||||
|
{
|
||||||
|
unsigned int result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(unsigned int)val : UINT_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val)
|
||||||
|
{
|
||||||
|
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val)
|
||||||
|
{
|
||||||
|
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
||||||
|
{
|
||||||
|
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastISizeToI*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Both operands are signed so the lowest rank operand will be promoted to
|
||||||
|
// match the highest rank operand.
|
||||||
|
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(val >= INT_MIN && val <= INT_MAX);
|
||||||
|
int result = DQN_CAST(int)DQN_CLAMP(val, INT_MIN, INT_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(val >= INT32_MIN && val <= INT32_MAX);
|
||||||
|
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(val >= INT64_MIN && val <= INT64_MAX);
|
||||||
|
int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastISizeToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// If the value is a negative integer, we clamp to 0. Otherwise, we know that
|
||||||
|
// the value is >=0, we can upcast safely to bounds check against the maximum
|
||||||
|
// allowed value.
|
||||||
|
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
||||||
|
{
|
||||||
|
unsigned int result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT_MAX))
|
||||||
|
result = DQN_CAST(unsigned int)val;
|
||||||
|
else
|
||||||
|
result = UINT_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
||||||
|
{
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||||
|
result = DQN_CAST(uint8_t)val;
|
||||||
|
else
|
||||||
|
result = UINT8_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
||||||
|
{
|
||||||
|
uint16_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||||
|
result = DQN_CAST(uint16_t)val;
|
||||||
|
else
|
||||||
|
result = UINT16_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
||||||
|
{
|
||||||
|
uint32_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT32_MAX))
|
||||||
|
result = DQN_CAST(uint32_t)val;
|
||||||
|
else
|
||||||
|
result = UINT32_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
||||||
|
{
|
||||||
|
uint64_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT64_MAX))
|
||||||
|
result = DQN_CAST(uint64_t)val;
|
||||||
|
else
|
||||||
|
result = UINT64_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastI64To*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Both operands are signed so the lowest rank operand will be promoted to
|
||||||
|
// match the highest rank operand.
|
||||||
|
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(int64_t val)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val)
|
||||||
|
{
|
||||||
|
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val)
|
||||||
|
{
|
||||||
|
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
||||||
|
{
|
||||||
|
DQN_CHECK(val >= INT32_MIN && val <= INT32_MAX);
|
||||||
|
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastIntTo*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val)
|
||||||
|
{
|
||||||
|
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||||
|
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
||||||
|
{
|
||||||
|
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||||
|
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
||||||
|
{
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||||
|
result = DQN_CAST(uint8_t)val;
|
||||||
|
else
|
||||||
|
result = UINT8_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val)
|
||||||
|
{
|
||||||
|
uint16_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||||
|
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||||
|
result = DQN_CAST(uint16_t)val;
|
||||||
|
else
|
||||||
|
result = UINT16_MAX;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(val) <= sizeof(uint32_t), "Sanity check to allow simplifying of casting");
|
||||||
|
uint32_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= 0))
|
||||||
|
result = DQN_CAST(uint32_t)val;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(val) <= sizeof(uint64_t), "Sanity check to allow simplifying of casting");
|
||||||
|
uint64_t result = 0;
|
||||||
|
if (DQN_CHECK(val >= 0))
|
||||||
|
result = DQN_CAST(uint64_t)val;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
|
||||||
|
// =================================================================================================
|
||||||
|
Dqn_ThreadScratch::Dqn_ThreadScratch(DQN_LEAK_TRACE_FUNCTION Dqn_ThreadContext *context, uint8_t context_index)
|
||||||
|
{
|
||||||
|
index = context_index;
|
||||||
|
allocator = context->temp_allocators[index];
|
||||||
|
arena = context->temp_arenas[index];
|
||||||
|
temp_memory = Dqn_Arena_BeginTempMemory(arena);
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
leak_site__ = DQN_LEAK_TRACE_ARG_NO_COMMA;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_ThreadScratch::~Dqn_ThreadScratch()
|
||||||
|
{
|
||||||
|
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||||
|
temp_arenas_stat[index] = arena->stats;
|
||||||
|
#endif
|
||||||
|
DQN_ASSERT(destructed == false);
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
Dqn_Arena_EndTempMemory_(leak_site__, temp_memory);
|
||||||
|
#else
|
||||||
|
Dqn_Arena_EndTempMemory_(temp_memory);
|
||||||
|
#endif
|
||||||
|
destructed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API uint32_t Dqn_Thread_GetID()
|
||||||
|
{
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
unsigned long result = GetCurrentThreadId();
|
||||||
|
#else
|
||||||
|
pid_t result = gettid();
|
||||||
|
assert(gettid() >= 0);
|
||||||
|
#endif
|
||||||
|
return (uint32_t)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA)
|
||||||
|
{
|
||||||
|
|
||||||
|
thread_local Dqn_ThreadContext result = {};
|
||||||
|
if (!result.init) {
|
||||||
|
result.init = true;
|
||||||
|
DQN_ASSERTF(dqn_library.lib_init, "Library must be initialised by calling Dqn_Library_Init(nullptr)");
|
||||||
|
|
||||||
|
// NOTE: Setup permanent arena
|
||||||
|
Dqn_ArenaCatalog *catalog = &dqn_library.arena_catalog;
|
||||||
|
result.allocator = Dqn_Arena_Allocator(result.arena);
|
||||||
|
result.arena = Dqn_ArenaCatalog_AllocF(catalog,
|
||||||
|
DQN_GIGABYTES(1) /*size*/,
|
||||||
|
DQN_KILOBYTES(64) /*commit*/,
|
||||||
|
"Thread %u Arena",
|
||||||
|
Dqn_Thread_GetID());
|
||||||
|
|
||||||
|
// NOTE: Setup temporary arenas
|
||||||
|
for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
|
||||||
|
result.temp_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog,
|
||||||
|
DQN_GIGABYTES(1) /*size*/,
|
||||||
|
DQN_KILOBYTES(64) /*commit*/,
|
||||||
|
"Thread %u Temp Arena %u",
|
||||||
|
Dqn_Thread_GetID(),
|
||||||
|
index);
|
||||||
|
result.temp_allocators[index] = Dqn_Arena_Allocator(result.temp_arenas[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||||
|
// manually pass it in?
|
||||||
|
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena)
|
||||||
|
{
|
||||||
|
static_assert(DQN_THREAD_CONTEXT_ARENAS < (uint8_t)-1, "We use UINT8_MAX as a sentinel value");
|
||||||
|
Dqn_ThreadContext *context = Dqn_Thread_GetContext_(DQN_LEAK_TRACE_ARG_NO_COMMA);
|
||||||
|
uint8_t context_index = (uint8_t)-1;
|
||||||
|
for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
|
||||||
|
Dqn_Arena *arena = context->temp_arenas[index];
|
||||||
|
if (!conflict_arena || arena != conflict_arena) {
|
||||||
|
context_index = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_ASSERT(context_index != (uint8_t)-1);
|
||||||
|
return Dqn_ThreadScratch(DQN_LEAK_TRACE_ARG context, context_index);
|
||||||
|
}
|
||||||
|
|
496
dqn_misc.h
Normal file
496
dqn_misc.h
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$DLIB] Dqn_Library | | Library run-time behaviour configuration
|
||||||
|
// =================================================================================================
|
||||||
|
//
|
||||||
|
// Book-keeping data for the library and allow customisation of certain features
|
||||||
|
// provided.
|
||||||
|
//
|
||||||
|
// NOTE: API
|
||||||
|
//
|
||||||
|
// @proc Dqn_Library_SetLogCallback
|
||||||
|
// @desc Update the default logging function, all logging functions will run through
|
||||||
|
// this callback
|
||||||
|
// @param[in] proc The new logging function, set to nullptr to revert back to
|
||||||
|
// the default logger.
|
||||||
|
// @param[in] user_data A user defined parameter to pass to the callback
|
||||||
|
//
|
||||||
|
// @proc Dqn_Library_SetLogFile
|
||||||
|
// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise
|
||||||
|
// point it to the FILE that you wish to write to.
|
||||||
|
//
|
||||||
|
// @proc Dqn_Library_DumpThreadContextArenaStat
|
||||||
|
// @desc Dump the per-thread arena statistics to the specified file
|
||||||
|
|
||||||
|
struct Dqn_Library
|
||||||
|
{
|
||||||
|
bool lib_init;
|
||||||
|
Dqn_TicketMutex lib_mutex;
|
||||||
|
|
||||||
|
Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine
|
||||||
|
void * log_user_data;
|
||||||
|
bool log_to_file; ///< Output logs to file as well as standard out
|
||||||
|
void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ?
|
||||||
|
Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time
|
||||||
|
bool log_no_colour; ///< Disable colours in the logging output
|
||||||
|
|
||||||
|
/// The backup arena to use if no arena is passed into Dqn_Library_Init
|
||||||
|
Dqn_Arena arena_catalog_backup_arena;
|
||||||
|
Dqn_ArenaCatalog arena_catalog;
|
||||||
|
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
Dqn_TicketMutex alloc_table_mutex;
|
||||||
|
Dqn_DSMap<Dqn_LeakTrace> alloc_table;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
LARGE_INTEGER win32_qpc_frequency;
|
||||||
|
Dqn_TicketMutex win32_bcrypt_rng_mutex;
|
||||||
|
void *win32_bcrypt_rng_handle;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||||
|
Dqn_TicketMutex thread_context_mutex;
|
||||||
|
Dqn_ArenaStat thread_context_arena_stats[256];
|
||||||
|
uint8_t thread_context_arena_stats_count;
|
||||||
|
#endif
|
||||||
|
} extern dqn_library;
|
||||||
|
|
||||||
|
// NOTE: Properties ================================================================================
|
||||||
|
DQN_API Dqn_Library *Dqn_Library_Init (Dqn_Arena *arena);
|
||||||
|
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
|
||||||
|
DQN_API void Dqn_Library_SetLogFile (void *file);
|
||||||
|
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path);
|
||||||
|
|
||||||
|
// NOTE: Leak Trace ================================================================================
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
DQN_API void Dqn_Library_LeakTraceAdd (Dqn_CallSite call_site, void *ptr, Dqn_usize size);
|
||||||
|
DQN_API void Dqn_Library_LeakTraceMarkFree (Dqn_CallSite call_site, void *ptr);
|
||||||
|
#else
|
||||||
|
#define Dqn_Library_LeakTraceAdd(...)
|
||||||
|
#define Dqn_Library_LeakTraceMarkFree(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$BITS] Dqn_Bit | | Bitset manipulation
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield);
|
||||||
|
DQN_API void Dqn_Bit_SetInplace(uint32_t *flags, uint32_t bitfield);
|
||||||
|
DQN_API bool Dqn_Bit_IsSet(uint32_t bits, uint32_t bits_to_set);
|
||||||
|
DQN_API bool Dqn_Bit_IsNotSet(uint32_t bits, uint32_t bits_to_check);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||||
|
// =================================================================================================
|
||||||
|
/// Assert the expression given in debug, whilst in release- assertion is
|
||||||
|
/// removed and the expression is evaluated and returned.
|
||||||
|
///
|
||||||
|
/// This function provides dual logic which allows handling of the condition
|
||||||
|
/// gracefully in release mode, but asserting in debug mode. This is an internal
|
||||||
|
/// function, prefer the @see DQN_CHECK macros.
|
||||||
|
///
|
||||||
|
/// @param assertion_expr[in] Expressin to assert on
|
||||||
|
/// @param fmt[in] Format string for providing a message on assertion
|
||||||
|
/// @return True if the expression evaluated to true, false otherwise.
|
||||||
|
DQN_API bool DQN_CHECKF_(bool assertion_expr, Dqn_CallSite call_site, char const *fmt, ...);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe Arithmetic
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Add 2 I64's together, safe asserting that the operation does not overflow.
|
||||||
|
/// @return The result of operation, INT64_MAX if it overflowed.
|
||||||
|
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b);
|
||||||
|
|
||||||
|
/// Multiply 2 I64's together, safe asserting that the operation does not
|
||||||
|
/// overflow.
|
||||||
|
/// @return The result of operation, IINT64_MAX if it overflowed.
|
||||||
|
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b);
|
||||||
|
|
||||||
|
/// Add 2 U64's together, safe asserting that the operation does not overflow.
|
||||||
|
/// @return The result of operation, UINT64_MAX if it overflowed.
|
||||||
|
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b);
|
||||||
|
|
||||||
|
/// Subtract 2 U64's together, safe asserting that the operation does not
|
||||||
|
/// overflow.
|
||||||
|
/// @return The result of operation, 0 if it overflowed.
|
||||||
|
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b);
|
||||||
|
|
||||||
|
/// Multiple 2 U64's together, safe asserting that the operation does not
|
||||||
|
/// overflow.
|
||||||
|
/// @return The result of operation, UINT64_MAX if it overflowed.
|
||||||
|
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b);
|
||||||
|
|
||||||
|
/// Subtract 2 U32's together, safe asserting that the operation does not
|
||||||
|
/// overflow.
|
||||||
|
/// @return The result of operation, 0 if it overflowed.
|
||||||
|
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastUSizeToI*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate a usize to int clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to I8 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT8_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to I16 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT16_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to I32 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT32_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to I64 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT64_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastU64ToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate a u64 to uint clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val);
|
||||||
|
|
||||||
|
/// Truncate a u64 to u8 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT8_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val);
|
||||||
|
|
||||||
|
/// Truncate a u64 to u16 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT16_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val);
|
||||||
|
|
||||||
|
/// Truncate a u64 to u32 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT32_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastUSizeToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate a usize to U8 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT8_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to U16 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT16_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to U32 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT32_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val);
|
||||||
|
|
||||||
|
/// Truncate a usize to U64 clamping the result to the max value of the desired
|
||||||
|
/// data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or UINT64_MAX if the value would go out of the
|
||||||
|
/// valid range when casted.
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastISizeToI*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate an isize to int clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT_MIN or INT_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to I8 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT8_MIN or INT8_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to I16 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT16_MIN or INT16_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to I32 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT32_MIN or INT32_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to I64 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT64_MIN or INT64_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastISizeToU*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate an isize to uint clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to U8 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT8_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to U16 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT16_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to U32 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT32_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an isize to U64 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT64_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastI64To*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate an I64 to isize clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or DQN_ISIZE_MIN or DQN_ISIZE_MAX if the value
|
||||||
|
/// would go out of the valid range when casted.
|
||||||
|
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(Dqn_isize val);
|
||||||
|
|
||||||
|
/// Truncate an I64 to I8 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT8_MIN or INT8_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val);
|
||||||
|
|
||||||
|
/// Truncate an I64 to I16 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT16_MIN or INT16_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val);
|
||||||
|
|
||||||
|
/// Truncate an I64 to I32 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT32_MIN or INT32_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val);
|
||||||
|
|
||||||
|
// NOTE: Dqn_Safe_SaturateCastIntTo*
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Truncate an int to I8 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT8_MIN or INT8_MAX if the value
|
||||||
|
/// would go out of the valid range when casted.
|
||||||
|
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val);
|
||||||
|
|
||||||
|
/// Truncate an int to I16 clamping the result to the min and max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or INT16_MIN or INT16_MAX if the value
|
||||||
|
/// would go out of the valid range when casted.
|
||||||
|
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val);
|
||||||
|
|
||||||
|
/// Truncate an int to U8 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT8_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val);
|
||||||
|
|
||||||
|
/// Truncate an int to U16 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT16_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val);
|
||||||
|
|
||||||
|
/// Truncate an int to U32 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT32_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val);
|
||||||
|
|
||||||
|
/// Truncate an int to U64 clamping the result to 0 and the max value of the
|
||||||
|
/// desired data type. Safe asserts if clamping occurs.
|
||||||
|
/// @return The truncated value or 0 or UINT64_MAX if the value would go
|
||||||
|
/// out of the valid range when casted.
|
||||||
|
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
|
||||||
|
// =================================================================================================
|
||||||
|
//
|
||||||
|
// Each thread is assigned in their thread-local storage (TLS) scratch and
|
||||||
|
// permanent arena allocators. These can be used for allocations with a lifetime
|
||||||
|
// scoped to the lexical scope or for storing data permanently using the arena
|
||||||
|
// paradigm.
|
||||||
|
//
|
||||||
|
// TLS in this implementation is implemented using the `thread_local` C/C++
|
||||||
|
// keyword.
|
||||||
|
//
|
||||||
|
// NOTE: API
|
||||||
|
//
|
||||||
|
// @proc Dqn_Thread_GetContext
|
||||||
|
// @desc Get the current thread's context- this contains all the metadata for managing
|
||||||
|
// the thread scratch data. In general you probably want Dqn_Thread_GetScratch()
|
||||||
|
// which ensures you get a usable scratch arena for temporary allocations
|
||||||
|
// without having to worry about selecting the right arena from the state.
|
||||||
|
//
|
||||||
|
// @proc Dqn_Thread_GetScratch
|
||||||
|
// @desc Retrieve the per-thread temporary arena allocator that is reset on scope
|
||||||
|
// exit.
|
||||||
|
//
|
||||||
|
// The scratch arena must be deconflicted with any existing arenas in the
|
||||||
|
// function to avoid trampling over each other's memory. Consider the situation
|
||||||
|
// where the scratch arena is passed into the function. Inside the function, if
|
||||||
|
// the same arena is reused then, if both arenas allocate, when the inner arena
|
||||||
|
// is reset, this will undo the passed in arena's allocations in the function.
|
||||||
|
//
|
||||||
|
// @param[in] conflict_arena A pointer to the arena currently being used in the
|
||||||
|
// function
|
||||||
|
|
||||||
|
#if !defined(DQN_THREAD_CONTEXT_ARENAS)
|
||||||
|
#define DQN_THREAD_CONTEXT_ARENAS 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Dqn_ThreadContext
|
||||||
|
{
|
||||||
|
Dqn_b32 init;
|
||||||
|
|
||||||
|
Dqn_Arena *arena; ///< Per thread arena
|
||||||
|
Dqn_Allocator allocator; ///< Allocator that uses the arena
|
||||||
|
|
||||||
|
/// Temp memory arena's for the calling thread
|
||||||
|
Dqn_Arena *temp_arenas[DQN_THREAD_CONTEXT_ARENAS];
|
||||||
|
|
||||||
|
/// Allocators that use the corresponding arena from the thread context.
|
||||||
|
/// Provided for convenience when interfacing with allocator interfaces.
|
||||||
|
Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS];
|
||||||
|
|
||||||
|
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||||
|
Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ThreadScratch
|
||||||
|
{
|
||||||
|
Dqn_ThreadScratch(DQN_LEAK_TRACE_FUNCTION Dqn_ThreadContext *context, uint8_t context_index);
|
||||||
|
~Dqn_ThreadScratch();
|
||||||
|
|
||||||
|
/// Index into the arena/allocator/stat array in the thread context
|
||||||
|
/// specifying what arena was assigned.
|
||||||
|
uint8_t index;
|
||||||
|
Dqn_Allocator allocator;
|
||||||
|
Dqn_Arena *arena;
|
||||||
|
Dqn_b32 destructed = false; /// Detect copies of the scratch
|
||||||
|
Dqn_ArenaTempMemory temp_memory;
|
||||||
|
#if defined(DQN_LEAK_TRACING)
|
||||||
|
Dqn_CallSite leak_site__;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: Context ===================================================================================
|
||||||
|
#define Dqn_Thread_GetContext() Dqn_Thread_GetContext_(DQN_LEAK_TRACE_NO_COMMA)
|
||||||
|
#define Dqn_Thread_GetScratch(conflict_arena) Dqn_Thread_GetScratch_(DQN_LEAK_TRACE conflict_arena)
|
||||||
|
DQN_API uint32_t Dqn_Thread_GetID();
|
||||||
|
|
||||||
|
// NOTE: Internal ==================================================================================
|
||||||
|
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA);
|
||||||
|
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$BSEA] Binary Search | |
|
||||||
|
// =================================================================================================
|
||||||
|
template <typename T>
|
||||||
|
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
enum Dqn_BinarySearchType
|
||||||
|
{
|
||||||
|
/// Index of the match. If no match is found, found is set to false and the
|
||||||
|
/// index is set to 0
|
||||||
|
Dqn_BinarySearchType_Match,
|
||||||
|
|
||||||
|
/// Index after the match. If no match is found, found is set to false and
|
||||||
|
/// the index is set to one past the closest match.
|
||||||
|
Dqn_BinarySearchType_OnePastMatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_BinarySearchResult
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
Dqn_usize index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Dqn_BinarySearchResult
|
||||||
|
Dqn_BinarySearch(T const *array,
|
||||||
|
Dqn_usize array_size,
|
||||||
|
T const &find,
|
||||||
|
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||||
|
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||||
|
{
|
||||||
|
bool result = lhs < rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Dqn_BinarySearchResult
|
||||||
|
Dqn_BinarySearch(T const *array,
|
||||||
|
Dqn_usize array_size,
|
||||||
|
T const &find,
|
||||||
|
Dqn_BinarySearchType type,
|
||||||
|
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||||
|
{
|
||||||
|
Dqn_BinarySearchResult result = {};
|
||||||
|
Dqn_usize head = 0;
|
||||||
|
Dqn_usize tail = array_size - 1;
|
||||||
|
if (array && array_size > 0) {
|
||||||
|
while (!result.found && head <= tail) {
|
||||||
|
Dqn_usize mid = (head + tail) / 2;
|
||||||
|
T const &value = array[mid];
|
||||||
|
if (less_than(find, value)) {
|
||||||
|
tail = mid - 1;
|
||||||
|
if (mid == 0)
|
||||||
|
break;
|
||||||
|
} else if (less_than(value, find)) {
|
||||||
|
head = mid + 1;
|
||||||
|
} else {
|
||||||
|
result.found = true;
|
||||||
|
result.index = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Dqn_BinarySearchType_OnePastMatch)
|
||||||
|
result.index = result.found ? result.index + 1 : tail + 1;
|
||||||
|
else
|
||||||
|
DQN_ASSERT(type == Dqn_BinarySearchType_Match);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
2228
dqn_platform.cpp
Normal file
2228
dqn_platform.cpp
Normal file
File diff suppressed because it is too large
Load Diff
475
dqn_platform.h
Normal file
475
dqn_platform.h
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$FSYS] Dqn_Fs | | Filesystem helpers
|
||||||
|
// =================================================================================================
|
||||||
|
enum Dqn_FsInfoType
|
||||||
|
{
|
||||||
|
Dqn_FsInfoType_Unknown,
|
||||||
|
Dqn_FsInfoType_Directory,
|
||||||
|
Dqn_FsInfoType_File,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_FsInfo
|
||||||
|
{
|
||||||
|
bool exists;
|
||||||
|
Dqn_FsInfoType type;
|
||||||
|
uint64_t create_time_in_s;
|
||||||
|
uint64_t last_write_time_in_s;
|
||||||
|
uint64_t last_access_time_in_s;
|
||||||
|
uint64_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: File System API
|
||||||
|
// =============================================================================
|
||||||
|
// TODO(dqn): We should have a Dqn_String8 interface and a CString interface
|
||||||
|
DQN_API bool Dqn_Fs_Exists(Dqn_String8 path);
|
||||||
|
DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path);
|
||||||
|
DQN_API Dqn_FsInfo Dqn_Fs_GetInfo(Dqn_String8 path);
|
||||||
|
DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite);
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Fs_MakeDir(Dqn_String8 path);
|
||||||
|
DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite);
|
||||||
|
|
||||||
|
// TODO(dqn): This doesn't work on directories unless you delete the files
|
||||||
|
// in that directory first.
|
||||||
|
DQN_API bool Dqn_Fs_Delete(Dqn_String8 path);
|
||||||
|
|
||||||
|
// NOTE: Read/Write Entire File API
|
||||||
|
// =============================================================================
|
||||||
|
// file_size: (Optional) The size of the file in bytes, the allocated buffer is (file_size + 1 [null terminator]) in bytes.
|
||||||
|
DQN_API bool Dqn_Fs_WriteCString8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size);
|
||||||
|
DQN_API bool Dqn_Fs_WriteString8(Dqn_String8 file_path, Dqn_String8 buffer);
|
||||||
|
|
||||||
|
/// Read a file at the specified path into memory.
|
||||||
|
/// @param[in] path Path to the file to read
|
||||||
|
/// @param[in] path_size The string size of the file path
|
||||||
|
/// @param[out] file_size (Optional) Pass a pointer to receive the number of bytes read
|
||||||
|
/// @param[in] allocator Allocator used to read the file to memory with
|
||||||
|
/// @return A cstring with the read file, null pointer on failure.
|
||||||
|
#define Dqn_Fs_ReadCString8(path, path_size, file_size, allocator) Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE path, path_size, file_size, allocator)
|
||||||
|
DQN_API char *Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE_FUNCTION char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator);
|
||||||
|
|
||||||
|
/// Read a file at the specified path into memory.
|
||||||
|
/// @param[in] file_path Path to the file to read
|
||||||
|
/// @param[in] allocator Allocator used to read the file to memory with
|
||||||
|
/// @return A string with the read file, invalid string on failure.
|
||||||
|
#define Dqn_Fs_ReadString8(path, allocator) Dqn_Fs_ReadString8_(DQN_LEAK_TRACE path, allocator)
|
||||||
|
DQN_API Dqn_String8 Dqn_Fs_ReadString8_(DQN_LEAK_TRACE_FUNCTION Dqn_String8 path, Dqn_Allocator allocator);
|
||||||
|
|
||||||
|
// NOTE: Read/Write File Stream API
|
||||||
|
// =============================================================================
|
||||||
|
struct Dqn_FsFile
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
|
char error[512];
|
||||||
|
uint16_t error_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_FsFileOpen
|
||||||
|
{
|
||||||
|
Dqn_FsFileOpen_CreateAlways, ///< Create file if it does not exist, otherwise, zero out the file and open
|
||||||
|
Dqn_FsFileOpen_OpenIfExist, ///< Open file at path only if it exists
|
||||||
|
Dqn_FsFileOpen_OpenAlways, ///< Open file at path, create file if it does not exist
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_FsFileAccess
|
||||||
|
{
|
||||||
|
Dqn_FsFileAccess_Read = 1 << 0,
|
||||||
|
Dqn_FsFileAccess_Write = 1 << 1,
|
||||||
|
Dqn_FsFileAccess_Execute = 1 << 2,
|
||||||
|
Dqn_FsFileAccess_AppendOnly = 1 << 3, ///< This flag cannot be combined with any other acess mode
|
||||||
|
Dqn_FsFileAccess_ReadWrite = Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write,
|
||||||
|
Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute,
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access);
|
||||||
|
DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size);
|
||||||
|
DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file);
|
||||||
|
|
||||||
|
// NOTE: Filesystem paths
|
||||||
|
// =============================================================================
|
||||||
|
#if !defined(Dqn_FsPathOSSeperator)
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
#define Dqn_FsPathOSSeperator "\\"
|
||||||
|
#else
|
||||||
|
#define Dqn_FsPathOSSeperator "/"
|
||||||
|
#endif
|
||||||
|
#define Dqn_FsPathOSSeperatorString DQN_STRING8(Dqn_FsPathOSSeperator)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Dqn_FsPathLink
|
||||||
|
{
|
||||||
|
Dqn_String8 string;
|
||||||
|
Dqn_FsPathLink *next;
|
||||||
|
Dqn_FsPathLink *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_FsPath
|
||||||
|
{
|
||||||
|
Dqn_FsPathLink *head;
|
||||||
|
Dqn_FsPathLink *tail;
|
||||||
|
Dqn_usize string_size;
|
||||||
|
uint16_t links_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
|
||||||
|
DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
|
||||||
|
DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path);
|
||||||
|
DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator);
|
||||||
|
DQN_API Dqn_String8 Dqn_FsPath_ConvertString8 (Dqn_Arena *arena, Dqn_String8 path);
|
||||||
|
|
||||||
|
#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/"))
|
||||||
|
#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\"))
|
||||||
|
|
||||||
|
#if !defined(Dqn_FsPath_Build)
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildBackSlash(arena, fs_path)
|
||||||
|
#else
|
||||||
|
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildFwdSlash(arena, fs_path)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$DATE] Dqn_Date | | Date-time helpers
|
||||||
|
// =================================================================================================
|
||||||
|
struct Dqn_DateHMSTimeString
|
||||||
|
{
|
||||||
|
char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")];
|
||||||
|
uint8_t date_size;
|
||||||
|
|
||||||
|
char hms[DQN_ARRAY_UCOUNT("HH:MM:SS")];
|
||||||
|
uint8_t hms_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_DateHMSTime
|
||||||
|
{
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t month;
|
||||||
|
int16_t year;
|
||||||
|
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minutes;
|
||||||
|
uint8_t seconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @return The current time at the point of invocation
|
||||||
|
DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow();
|
||||||
|
DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeStringNow(char date_separator = '-', char hms_separator = ':');
|
||||||
|
DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString(Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':');
|
||||||
|
|
||||||
|
// return: The time elapsed since Unix epoch (1970-01-01T00:00:00Z) in seconds
|
||||||
|
DQN_API uint64_t Dqn_Date_EpochTime();
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$W32H] Win32 minimal header | DQN_NO_WIN32_MINIMAL_HEADER | Minimal windows.h subset
|
||||||
|
// =================================================================================================
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
#if !defined(DQN_NO_WIN32_MINIMAL_HEADER) && !defined(_INC_WINDOWS)
|
||||||
|
// Taken from Windows.h
|
||||||
|
// typedef unsigned long DWORD;
|
||||||
|
// typedef unsigned short WORD;
|
||||||
|
// typedef int BOOL;
|
||||||
|
// typedef void * HWND;
|
||||||
|
// typedef void * HANDLE;
|
||||||
|
// typedef long NTSTATUS;
|
||||||
|
|
||||||
|
typedef void * HMODULE;
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
unsigned long LowPart;
|
||||||
|
long HighPart;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
unsigned long LowPart;
|
||||||
|
long HighPart;
|
||||||
|
} u;
|
||||||
|
uint64_t QuadPart;
|
||||||
|
} LARGE_INTEGER;
|
||||||
|
#endif // !defined(DQN_NO_WIN32_MINIMAL_HEADER) && !defined(_INC_WINDOWS)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$WIND] Dqn_Win | | Windows OS helpers
|
||||||
|
// =================================================================================================
|
||||||
|
struct Dqn_WinErrorMsg
|
||||||
|
{
|
||||||
|
unsigned long code;
|
||||||
|
char data[DQN_KILOBYTES(64) - 1]; // Maximum error size
|
||||||
|
unsigned long size;
|
||||||
|
};
|
||||||
|
DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg);
|
||||||
|
DQN_API Dqn_WinErrorMsg Dqn_Win_LastError();
|
||||||
|
|
||||||
|
/// Call once at application start-up to ensure that the application is DPI
|
||||||
|
/// aware on Windows and ensure that application UI is scaled up appropriately
|
||||||
|
/// for the monitor.
|
||||||
|
DQN_API void Dqn_Win_MakeProcessDPIAware();
|
||||||
|
|
||||||
|
// NOTE: Windows String8 To String16
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Convert a UTF8 to UTF16 string.
|
||||||
|
///
|
||||||
|
/// The exact size buffer required for this function can be determined by
|
||||||
|
/// calling this function with the 'dest' set to null and 'dest_size' set to 0,
|
||||||
|
/// the return size is the size required for conversion not-including space for
|
||||||
|
/// the null-terminator. This function *always* null-terminates the input
|
||||||
|
/// buffer.
|
||||||
|
///
|
||||||
|
/// @return The number of u16's written/required for conversion. 0 if there was
|
||||||
|
/// a conversion error and can be queried using 'Dqn_Win_LastError'
|
||||||
|
DQN_API int Dqn_Win_CString8ToCString16(const char *src, int src_size, wchar_t *dest, int dest_size);
|
||||||
|
DQN_API int Dqn_Win_String8ToCString16(Dqn_String8 src, wchar_t *dest, int dest_size);
|
||||||
|
DQN_API Dqn_String16 Dqn_Win_String8ToString16Allocator(Dqn_String8 src, Dqn_Allocator allocator);
|
||||||
|
|
||||||
|
// NOTE: Windows String16 To String8
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Convert a UTF16 to UTF8 string.
|
||||||
|
///
|
||||||
|
/// The exact size buffer required for this function can be determined by
|
||||||
|
/// calling this function with the 'dest' set to null and 'dest_size' set to 0,
|
||||||
|
/// the return size is the size required for conversion not-including space for
|
||||||
|
/// the null-terminator. This function *always* null-terminates the input
|
||||||
|
/// buffer.
|
||||||
|
///
|
||||||
|
/// @return The number of u8's written/required for conversion. 0 if there was
|
||||||
|
/// a conversion error and can be queried using 'Dqn_Win_LastError'
|
||||||
|
DQN_API int Dqn_Win_CString16ToCString8(const wchar_t *src, int src_size, char *dest, int dest_size);
|
||||||
|
DQN_API Dqn_String8 Dqn_Win_CString16ToString8Allocator(const wchar_t *src, int src_size, Dqn_Allocator allocator);
|
||||||
|
DQN_API int Dqn_Win_String16ToCString8(Dqn_String16 src, char *dest, int dest_size);
|
||||||
|
DQN_API Dqn_String8 Dqn_Win_String16ToString8Allocator(Dqn_String16 src, Dqn_Allocator allocator);
|
||||||
|
|
||||||
|
// NOTE: Windows Executable Directory
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/// Evaluate the current executable's directory that is running when this
|
||||||
|
/// function is called.
|
||||||
|
/// @param[out] buffer The buffer to write the executable directory into. Set
|
||||||
|
/// this to null to calculate the required buffer size for the directory.
|
||||||
|
/// @param[in] size The size of the buffer given. Set this to 0 to calculate the
|
||||||
|
/// required buffer size for the directory.
|
||||||
|
/// @return The length of the executable directory string. If this return value
|
||||||
|
/// exceeds the capacity of the 'buffer', the 'buffer' is untouched.
|
||||||
|
DQN_API Dqn_usize Dqn_Win_EXEDirW(wchar_t *buffer, Dqn_usize size);
|
||||||
|
DQN_API Dqn_String16 Dqn_Win_EXEDirWArena(Dqn_Arena *arena);
|
||||||
|
|
||||||
|
// @param[in] size (Optional) The size of the current directory string returned
|
||||||
|
// @param[in] suffix (Optional) A suffix to append to the current working directory
|
||||||
|
// @param[in] suffix_size (Optional) The size of the suffix to append
|
||||||
|
DQN_API Dqn_String8 Dqn_Win_WorkingDir(Dqn_Allocator allocator, Dqn_String8 suffix);
|
||||||
|
DQN_API Dqn_String16 Dqn_Win_WorkingDirW(Dqn_Allocator allocator, Dqn_String16 suffix);
|
||||||
|
|
||||||
|
struct Dqn_Win_FolderIteratorW
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
|
Dqn_String16 file_name;
|
||||||
|
wchar_t file_name_buf[512];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_Win_FolderIterator
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
|
Dqn_String8 file_name;
|
||||||
|
char file_name_buf[512];
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Win_FolderIterate(Dqn_String8 path, Dqn_Win_FolderIterator *it);
|
||||||
|
DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it);
|
||||||
|
|
||||||
|
#if !defined(DQN_NO_WINNET)
|
||||||
|
// =================================================================================================
|
||||||
|
// [$WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers
|
||||||
|
// =================================================================================================
|
||||||
|
enum Dqn_WinNetHandleState
|
||||||
|
{
|
||||||
|
Dqn_WinNetHandleState_Invalid,
|
||||||
|
Dqn_WinNetHandleState_Initialised,
|
||||||
|
Dqn_WinNetHandleState_HttpMethodReady,
|
||||||
|
Dqn_WinNetHandleState_RequestFailed,
|
||||||
|
Dqn_WinNetHandleState_RequestGood,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number of bytes each pump of the connection downloads at most. If this is
|
||||||
|
// zero we default to DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE.
|
||||||
|
#if !defined(DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE)
|
||||||
|
#define DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Dqn_WinNetHandle
|
||||||
|
{
|
||||||
|
// NOTE: We copy out the host name because it needs to be null-terminated.
|
||||||
|
// Luckily, we can assume a DNS domain won't exceed 256 characters so this
|
||||||
|
// will generally always work.
|
||||||
|
char host_name[256];
|
||||||
|
int host_name_size;
|
||||||
|
|
||||||
|
// NOTE: Everything after the domain/host name part of the string i.e. the
|
||||||
|
// '/test' part of the full url 'mywebsite.com/test'.
|
||||||
|
// TODO(dqn): I don't want to make our network API allocate here so we don't
|
||||||
|
// copy the string since we require that the string is null-terminated so
|
||||||
|
// then taking a pointer to the input string should work .. maybe this is
|
||||||
|
// ok?
|
||||||
|
char *url;
|
||||||
|
int url_size;
|
||||||
|
|
||||||
|
// NOTE: docs.microsoft.com/en-us/windows/win32/wininet/setting-and-retrieving-internet-options#scope-of-hinternet-handle
|
||||||
|
// These handles have three levels:
|
||||||
|
//
|
||||||
|
// The root HINTERNET handle (created by a call to InternetOpen) would contain all the Internet options that affect this instance of WinINet.
|
||||||
|
// HINTERNET handles that connect to a server (created by a call to InternetConnect)
|
||||||
|
// HINTERNET handles associated with a resource or enumeration of resources on a particular server.
|
||||||
|
//
|
||||||
|
// More detailed information about the HINTERNET dependency is listed here
|
||||||
|
// NOTE: https://docs.microsoft.com/en-us/windows/win32/wininet/appendix-a-hinternet-handles
|
||||||
|
void *internet_open_handle;
|
||||||
|
void *internet_connect_handle;
|
||||||
|
void *http_handle;
|
||||||
|
Dqn_WinNetHandleState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(dqn): Useful options to expose in the handle
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/wininet/option-flags
|
||||||
|
// INTERNET_OPTION_CONNECT_RETRIES -- default is 5 retries
|
||||||
|
// INTERNET_OPTION_CONNECT_TIMEOUT -- milliseconds
|
||||||
|
// INTERNET_OPTION_RECEIVE_TIMEOUT
|
||||||
|
// INTERNET_OPTION_SEND_TIMEOUT
|
||||||
|
|
||||||
|
// Setup a handle to the URL with the given HTTP verb.
|
||||||
|
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString(char const *url, int url_size);
|
||||||
|
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit(Dqn_String8 url);
|
||||||
|
|
||||||
|
// Setup a handle to the URL with the given HTTP verb.
|
||||||
|
//
|
||||||
|
// This function is the same as calling Dqn_Win_NetHandleInit() followed by
|
||||||
|
// Dqn_Win_NetHandleSetHTTPMethod().
|
||||||
|
//
|
||||||
|
// @param http_method The HTTP request type, e.g. "GET" or "POST" e.t.c
|
||||||
|
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCString(char const *url, int url_size, char const *http_method);
|
||||||
|
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod(Dqn_String8 url, Dqn_String8 http_method);
|
||||||
|
|
||||||
|
DQN_API void Dqn_Win_NetHandleClose(Dqn_WinNetHandle *handle);
|
||||||
|
DQN_API bool Dqn_Win_NetHandleIsValid(Dqn_WinNetHandle const *handle);
|
||||||
|
DQN_API void Dqn_Win_NetHandleSetUserAgentCString(Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size);
|
||||||
|
|
||||||
|
// Set the HTTP request method for the given handle. This function can be used
|
||||||
|
// on a pre-existing valid handle that has at the minimum been initialised.
|
||||||
|
DQN_API bool Dqn_Win_NetHandleSetHTTPMethod(Dqn_WinNetHandle *handle, char const *method);
|
||||||
|
|
||||||
|
enum Dqn_WinNetHandleRequestHeaderFlag
|
||||||
|
{
|
||||||
|
Dqn_WinNetHandleRequestHeaderFlag_Add,
|
||||||
|
Dqn_WinNetHandleRequestHeaderFlag_AddIfNew,
|
||||||
|
Dqn_WinNetHandleRequestHeaderFlag_Replace,
|
||||||
|
Dqn_WinNetHandleRequestHeaderFlag_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle, char const *header, int header_size, uint32_t mode);
|
||||||
|
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderString8(Dqn_WinNetHandle *handle, Dqn_String8 header, uint32_t mode);
|
||||||
|
|
||||||
|
struct Dqn_WinNetHandleResponse
|
||||||
|
{
|
||||||
|
Dqn_String8 raw_headers;
|
||||||
|
Dqn_String8 *headers;
|
||||||
|
Dqn_usize headers_size;
|
||||||
|
|
||||||
|
// NOTE: Headers pulled from the 'raw_headers' for convenience
|
||||||
|
uint64_t content_length;
|
||||||
|
Dqn_String8 content_type;
|
||||||
|
};
|
||||||
|
DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest(Dqn_WinNetHandle *handle, Dqn_Allocator allocator, char const *post_data, unsigned long post_data_size);
|
||||||
|
|
||||||
|
DQN_API bool Dqn_Win_NetHandlePump(Dqn_WinNetHandle *handle, char *dest, int dest_size, size_t *download_size);
|
||||||
|
DQN_API char * Dqn_Win_NetHandlePumpCString8(Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size);
|
||||||
|
DQN_API Dqn_String8 Dqn_Win_NetHandlePumpString8(Dqn_WinNetHandle *handle, Dqn_Arena *arena);
|
||||||
|
|
||||||
|
DQN_API void Dqn_Win_NetHandlePumpToCRTFile(Dqn_WinNetHandle *handle, FILE *file);
|
||||||
|
DQN_API char *Dqn_Win_NetHandlePumpToAllocCString(Dqn_WinNetHandle *handle, size_t *download_size);
|
||||||
|
DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString(Dqn_WinNetHandle *handle);
|
||||||
|
#endif // !defined(DQN_NO_WINNET)
|
||||||
|
#endif // defined(DQN_OS_WIN32)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs
|
||||||
|
// =================================================================================================
|
||||||
|
/// Generate cryptographically secure bytes
|
||||||
|
DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size);
|
||||||
|
|
||||||
|
// return: The directory without the trailing '/' or ('\' for windows). Empty
|
||||||
|
// string with a nullptr if it fails.
|
||||||
|
DQN_API Dqn_String8 Dqn_OS_EXEDir(Dqn_Allocator allocator);
|
||||||
|
|
||||||
|
DQN_API void Dqn_OS_SleepMs(Dqn_uint milliseconds);
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_PerfCounterMicroS(uint64_t begin, uint64_t end);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||||
|
|
||||||
|
/// Record time between two time-points using the OS's performance counter.
|
||||||
|
struct Dqn_OSTimer
|
||||||
|
{
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t end;
|
||||||
|
};
|
||||||
|
|
||||||
|
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin();
|
||||||
|
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_TimerMicroS (Dqn_OSTimer timer);
|
||||||
|
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
|
||||||
|
|
||||||
|
// OS_TimedBlock provides a extremely primitive way of measuring the duration of
|
||||||
|
// code blocks, by sprinkling DQN_OS_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_OS_TIMED_BLOCK_INIT("Profiling Region", 32); // name, records to allocate
|
||||||
|
DQN_OS_TIMED_BLOCK_RECORD("a");
|
||||||
|
for (int unused1_ = 0; unused1_ < 1000000; unused1_++)
|
||||||
|
{
|
||||||
|
for (int unused2_ = 0; unused2_ < 1000000; unused2_++)
|
||||||
|
{
|
||||||
|
(void)unused1_;
|
||||||
|
(void)unused2_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DQN_OS_TIMED_BLOCK_RECORD("b");
|
||||||
|
DQN_OS_TIMED_BLOCK_DUMP;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
struct Dqn_OSTimedBlock
|
||||||
|
{
|
||||||
|
char const *label;
|
||||||
|
uint64_t tick;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialise a timing block region,
|
||||||
|
#define DQN_OS_TIMED_BLOCK_INIT(label, size) \
|
||||||
|
Dqn_OSTimedBlock timings_[size]; \
|
||||||
|
Dqn_usize timings_size_ = 0; \
|
||||||
|
DQN_OS_TIMED_BLOCK_RECORD(label)
|
||||||
|
|
||||||
|
// Add a timing record at the current location this macro is invoked.
|
||||||
|
// DQN_OS_TIMED_BLOCK_INIT must have been called in a scope visible to the macro
|
||||||
|
// prior.
|
||||||
|
// label: The label to give to the timing record
|
||||||
|
#define DQN_OS_TIMED_BLOCK_RECORD(label) timings_[timings_size_++] = {label, Dqn_OS_PerfCounterNow()}
|
||||||
|
|
||||||
|
// Dump the timing block via Dqn_Log
|
||||||
|
#define DQN_OS_TIMED_BLOCK_DUMP \
|
||||||
|
DQN_ASSERTF(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_OSTimedBlock t1 = timings_[timings_index_ + 0]; \
|
||||||
|
Dqn_OSTimedBlock t2 = timings_[timings_index_ + 1]; \
|
||||||
|
DQN_LOG_D("%s -> %s: %fms", t1.label, t2.label, Dqn_OS_PerfCounterMs(t1.tick, t2.tick)); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (timings_size_ >= 1) { \
|
||||||
|
Dqn_OSTimedBlock t1 = timings_[0]; \
|
||||||
|
Dqn_OSTimedBlock t2 = timings_[timings_size_ - 1]; \
|
||||||
|
DQN_LOG_D("%s -> %s (total): %fms", t1.label, t2.label, Dqn_OS_PerfCounterMs(t1.tick, t2.tick));\
|
||||||
|
}
|
345
dqn_print.cpp
Normal file
345
dqn_print.cpp
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$PRIN] Dqn_Print | | Printing
|
||||||
|
// =================================================================================================
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
|
||||||
|
{
|
||||||
|
Dqn_PrintStyle result = {};
|
||||||
|
result.bold = bold;
|
||||||
|
result.colour = true;
|
||||||
|
result.r = r;
|
||||||
|
result.g = g;
|
||||||
|
result.b = b;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32(uint32_t rgb, Dqn_PrintBold bold)
|
||||||
|
{
|
||||||
|
uint8_t r = (rgb >> 24) & 0xFF;
|
||||||
|
uint8_t g = (rgb >> 16) & 0xFF;
|
||||||
|
uint8_t b = (rgb >> 8) & 0xFF;
|
||||||
|
Dqn_PrintStyle result = Dqn_Print_StyleColour(r, g, b, bold);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold()
|
||||||
|
{
|
||||||
|
Dqn_PrintStyle result = {};
|
||||||
|
result.bold = Dqn_PrintBold_Yes;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
|
||||||
|
{
|
||||||
|
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
|
||||||
|
|
||||||
|
#if defined(DQN_OS_WIN32)
|
||||||
|
// NOTE: Get the output handles from kernel
|
||||||
|
// =========================================================================
|
||||||
|
thread_local void *std_out_print_handle = nullptr;
|
||||||
|
thread_local void *std_err_print_handle = nullptr;
|
||||||
|
thread_local bool std_out_print_to_console = false;
|
||||||
|
thread_local bool std_err_print_to_console = false;
|
||||||
|
|
||||||
|
if (!std_out_print_handle) {
|
||||||
|
unsigned long mode = 0; (void)mode;
|
||||||
|
std_out_print_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
std_out_print_to_console = GetConsoleMode(std_out_print_handle, &mode) != 0;
|
||||||
|
|
||||||
|
std_err_print_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Select the output handle
|
||||||
|
// =========================================================================
|
||||||
|
void *print_handle = std_out_print_handle;
|
||||||
|
bool print_to_console = std_out_print_to_console;
|
||||||
|
if (std_handle == Dqn_PrintStd_Err) {
|
||||||
|
print_handle = std_err_print_handle;
|
||||||
|
print_to_console = std_err_print_to_console;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Write the string
|
||||||
|
// =========================================================================
|
||||||
|
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
|
||||||
|
unsigned long bytes_written = 0; (void)bytes_written;
|
||||||
|
if (print_to_console) {
|
||||||
|
WriteConsoleA(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||||
|
} else {
|
||||||
|
WriteFile(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STRING_FMT(string));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
|
||||||
|
{
|
||||||
|
if (string.data && string.size) {
|
||||||
|
if (style.colour)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
|
||||||
|
if (style.bold == Dqn_PrintBold_Yes)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
|
||||||
|
Dqn_Print_Std(std_handle, string);
|
||||||
|
if (style.colour || style.bold == Dqn_PrintBold_Yes)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
|
||||||
|
{
|
||||||
|
Dqn_String8 string = {};
|
||||||
|
string.data = DQN_CAST(char *)buf;
|
||||||
|
string.size = len;
|
||||||
|
|
||||||
|
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
|
||||||
|
Dqn_Print_Std(std_handle, string);
|
||||||
|
return (char *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
char buffer[STB_SPRINTF_MIN];
|
||||||
|
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
if (fmt) {
|
||||||
|
if (style.colour)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
|
||||||
|
if (style.bold == Dqn_PrintBold_Yes)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
|
||||||
|
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||||
|
if (style.colour || style.bold == Dqn_PrintBold_Yes)
|
||||||
|
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string)
|
||||||
|
{
|
||||||
|
Dqn_Print_Std(std_handle, string);
|
||||||
|
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Print_StdLnFV(std_handle, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||||
|
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
|
||||||
|
{
|
||||||
|
Dqn_Print_StdStyle(std_handle, style, string);
|
||||||
|
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Print_StdLnFVStyle(std_handle, style, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||||
|
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_String8 Dqn_Print_ESCColourString(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
thread_local char buffer[32];
|
||||||
|
buffer[0] = 0;
|
||||||
|
Dqn_String8 result = {};
|
||||||
|
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
|
||||||
|
DQN_ARRAY_UCOUNT(buffer),
|
||||||
|
"\x1b[%d;2;%u;%u;%um",
|
||||||
|
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
|
||||||
|
r, g, b);
|
||||||
|
result.data = buffer;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value)
|
||||||
|
{
|
||||||
|
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
|
||||||
|
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
|
||||||
|
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
|
||||||
|
Dqn_String8 result = Dqn_Print_ESCColourString(colour, r, g, b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$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)
|
||||||
|
{
|
||||||
|
(void)log_type;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
// NOTE: Open log file for appending if requested
|
||||||
|
// =========================================================================
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||||
|
if (dqn_library.log_to_file && !dqn_library.log_file) {
|
||||||
|
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||||
|
Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.allocator);
|
||||||
|
Dqn_String8 log_file = Dqn_String8_InitF(scratch.allocator, "%.*s/dqn.log", DQN_STRING_FMT(exe_dir));
|
||||||
|
fopen_s(DQN_CAST(FILE **)&dqn_library.log_file, log_file.data, "a");
|
||||||
|
}
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||||
|
|
||||||
|
// NOTE: Generate the log header
|
||||||
|
// =========================================================================
|
||||||
|
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||||
|
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
|
||||||
|
!dqn_library.log_no_colour,
|
||||||
|
type,
|
||||||
|
log_type,
|
||||||
|
call_site,
|
||||||
|
fmt,
|
||||||
|
args);
|
||||||
|
|
||||||
|
// NOTE: Print log
|
||||||
|
// =========================================================================
|
||||||
|
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||||
|
|
||||||
|
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||||
|
if (dqn_library.log_to_file) {
|
||||||
|
fprintf(DQN_CAST(FILE *)dqn_library.log_file, "%.*s\n", DQN_STRING_FMT(log_line));
|
||||||
|
}
|
||||||
|
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_LogProc *logging_function = dqn_library.log_callback ? dqn_library.log_callback : Dqn_Log_FVDefault_;
|
||||||
|
logging_function(type, -1 /*log_type*/, dqn_library.log_user_data, call_site, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Log_FVCallSite(type, call_site, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_String8 type_string = DQN_STRING8("DQN-BAD-LOG-TYPE");
|
||||||
|
switch (type) {
|
||||||
|
case Dqn_LogType_Error: type_string = DQN_STRING8("ERROR"); break;
|
||||||
|
case Dqn_LogType_Info: type_string = DQN_STRING8("INFO"); break;
|
||||||
|
case Dqn_LogType_Warning: type_string = DQN_STRING8("WARN"); break;
|
||||||
|
case Dqn_LogType_Debug: type_string = DQN_STRING8("DEBUG"); break;
|
||||||
|
case Dqn_LogType_Count: type_string = DQN_STRING8("BADXX"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_LogProc *logging_function = dqn_library.log_callback ? dqn_library.log_callback : Dqn_Log_FVDefault_;
|
||||||
|
logging_function(type_string, type /*log_type*/, dqn_library.log_user_data, call_site, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_Log_TypeFVCallSite(type, call_site, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
135
dqn_print.h
Normal file
135
dqn_print.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// [$PRIN] Dqn_Print | | Console printing
|
||||||
|
// =================================================================================================
|
||||||
|
enum Dqn_PrintStd
|
||||||
|
{
|
||||||
|
Dqn_PrintStd_Out,
|
||||||
|
Dqn_PrintStd_Err,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_PrintBold
|
||||||
|
{
|
||||||
|
Dqn_PrintBold_No,
|
||||||
|
Dqn_PrintBold_Yes,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_PrintStyle
|
||||||
|
{
|
||||||
|
Dqn_PrintBold bold;
|
||||||
|
bool colour;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Dqn_PrintESCColour
|
||||||
|
{
|
||||||
|
Dqn_PrintESCColour_Fg,
|
||||||
|
Dqn_PrintESCColour_Bg,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: Print Style ===============================================================================
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
|
||||||
|
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
|
||||||
|
|
||||||
|
// NOTE: Print Standard Out ========================================================================
|
||||||
|
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
|
||||||
|
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
|
||||||
|
|
||||||
|
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
|
||||||
|
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
|
||||||
|
|
||||||
|
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
|
||||||
|
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
|
||||||
|
|
||||||
|
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
|
||||||
|
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
|
||||||
|
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
|
||||||
|
|
||||||
|
// NOTE: Print =====================================================================================
|
||||||
|
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||||
|
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||||
|
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||||
|
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||||
|
|
||||||
|
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||||
|
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||||
|
|
||||||
|
// NOTE: ANSI Formatting Codes =====================================================================
|
||||||
|
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value);
|
||||||
|
|
||||||
|
#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b)
|
||||||
|
#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b)
|
||||||
|
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data
|
||||||
|
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data
|
||||||
|
|
||||||
|
#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value)
|
||||||
|
#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value)
|
||||||
|
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data
|
||||||
|
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data
|
||||||
|
|
||||||
|
#define Dqn_Print_ESCReset "\x1b[0m"
|
||||||
|
#define Dqn_Print_ESCBold "\x1b[1m"
|
||||||
|
#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset)
|
||||||
|
#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold)
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// [$LLOG] Dqn_Log | | Library logging
|
||||||
|
// =================================================================================================
|
||||||
|
enum Dqn_LogType
|
||||||
|
{
|
||||||
|
Dqn_LogType_Debug,
|
||||||
|
Dqn_LogType_Info,
|
||||||
|
Dqn_LogType_Warning,
|
||||||
|
Dqn_LogType_Error,
|
||||||
|
Dqn_LogType_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// RGBA
|
||||||
|
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
|
||||||
|
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
|
||||||
|
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
|
||||||
|
|
||||||
|
/// The logging procedure of the library. Users can override the default logging
|
||||||
|
/// function by setting the logging function pointer in Dqn_Library. This
|
||||||
|
/// function will be invoked every time a log is recorded using the following
|
||||||
|
/// functions.
|
||||||
|
///
|
||||||
|
/// @param[in] log_type This value is one of the Dqn_LogType values if the log
|
||||||
|
/// was generated from one of the default categories. -1 if the log is not from
|
||||||
|
/// one of the default categories.
|
||||||
|
typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||||
|
|
||||||
|
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||||
|
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
|
||||||
|
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
|
||||||
|
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
|
||||||
|
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
|
||||||
|
|
||||||
|
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||||
|
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite(type, DQN_CALL_SITE, fmt, ## __VA_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__)
|
||||||
|
|
||||||
|
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_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_FCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...);
|
||||||
|
|
2948
dqn_strings.cpp
Normal file
2948
dqn_strings.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1151
dqn_strings.h
Normal file
1151
dqn_strings.h
Normal file
File diff suppressed because it is too large
Load Diff
29
readme.md
29
readme.md
@ -1,3 +1,28 @@
|
|||||||
# Dqn
|
# Dqn
|
||||||
Personal utility library that provides allocator aware data structures, custom
|
|
||||||
memory allocators and various miscellaneous helpers.
|
Personal standard library that provides allocator aware data structures, custom
|
||||||
|
memory allocators and various miscellaneous helpers for prototyping. The library
|
||||||
|
is a unity-build style library where data structures and functions are separated
|
||||||
|
by category into files for organisation. You only need to include `dqn.h` which
|
||||||
|
amalgamates all the files into one translation unit.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build with this library, copy all the `*.[h|cpp]` files at the root of the
|
||||||
|
repository and in one header file,
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "dqn.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Which includes all other files and their declaration into your header. In one
|
||||||
|
`.cpp` file defined the implementation macro to enable the implementation of the
|
||||||
|
header in that translation unit,
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define DQN_IMPLEMENTATION
|
||||||
|
#include "dqn.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure that the folder containing the files is part of the include search path
|
||||||
|
for the compiler for the amalgamated `dqn.h` to successfully locate the files.
|
||||||
|
Loading…
Reference in New Issue
Block a user