2025 spring cleaning
This commit is contained in:
parent
70d0889b00
commit
2371297dda
743
Base/dn_base.cpp
Normal file
743
Base/dn_base.cpp
Normal file
@ -0,0 +1,743 @@
|
||||
#define DN_BASE_CPP
|
||||
|
||||
#include "../dn_clangd.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
// $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$$$$$$\ | \____$$\ $$ _____|$$ __$$\
|
||||
// $$ __$$\ $$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ | $$ |$$ __$$ | \____$$\ $$ ____|
|
||||
// $$$$$$$ |\$$$$$$$ |$$$$$$$ |\$$$$$$$\
|
||||
// \_______/ \_______|\_______/ \_______|
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#if defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
||||
|
||||
DN_API DN_CPUIDResult DN_CPU_ID(DN_CPUIDArgs args)
|
||||
{
|
||||
DN_CPUIDResult result = {};
|
||||
__cpuidex(result.values, args.eax, args.ecx);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_CPU_HasFeatureArray(DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size)
|
||||
{
|
||||
DN_USize result = 0;
|
||||
DN_USize const BITS = sizeof(report->features[0]) * 8;
|
||||
DN_ForIndexU(feature_index, features_size) {
|
||||
DN_CPUFeatureQuery *query = features + feature_index;
|
||||
DN_USize chunk_index = query->feature / BITS;
|
||||
DN_USize chunk_bit = query->feature % BITS;
|
||||
DN_U64 chunk = report->features[chunk_index];
|
||||
query->available = chunk & (1ULL << chunk_bit);
|
||||
result += DN_CAST(int) query->available;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_CPU_HasFeature(DN_CPUReport const *report, DN_CPUFeature feature)
|
||||
{
|
||||
DN_CPUFeatureQuery query = {};
|
||||
query.feature = feature;
|
||||
bool result = DN_CPU_HasFeatureArray(report, &query, 1) == 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_CPU_HasAllFeatures(DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size)
|
||||
{
|
||||
bool result = true;
|
||||
for (DN_USize index = 0; result && index < features_size; index++)
|
||||
result &= DN_CPU_HasFeature(report, features[index]);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_CPU_SetFeature(DN_CPUReport *report, DN_CPUFeature feature)
|
||||
{
|
||||
DN_Assert(feature < DN_CPUFeature_Count);
|
||||
DN_USize const BITS = sizeof(report->features[0]) * 8;
|
||||
DN_USize chunk_index = feature / BITS;
|
||||
DN_USize chunk_bit = feature % BITS;
|
||||
report->features[chunk_index] |= (1ULL << chunk_bit);
|
||||
}
|
||||
|
||||
DN_API DN_CPUReport DN_CPU_Report()
|
||||
{
|
||||
DN_CPUReport result = {};
|
||||
DN_CPUIDResult fn_0000_[500] = {};
|
||||
DN_CPUIDResult fn_8000_[500] = {};
|
||||
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
|
||||
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
|
||||
|
||||
// NOTE: Query standard/extended numbers ///////////////////////////////////////////////////////
|
||||
{
|
||||
DN_CPUIDArgs args = {};
|
||||
|
||||
// NOTE: Query standard function (e.g. eax = 0x0) for function count + cpu vendor
|
||||
args = {};
|
||||
fn_0000_[0] = DN_CPU_ID(args);
|
||||
|
||||
// NOTE: Query extended function (e.g. eax = 0x8000'0000) for function count + cpu vendor
|
||||
args = {};
|
||||
args.eax = DN_CAST(int) EXTENDED_FUNC_BASE_EAX;
|
||||
fn_8000_[0] = DN_CPU_ID(args);
|
||||
}
|
||||
|
||||
// NOTE: Extract function count ////////////////////////////////////////////////////////////////
|
||||
int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax;
|
||||
int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax;
|
||||
|
||||
// NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
|
||||
{
|
||||
DN_AssertF((STANDARD_FUNC_MAX_EAX + 1) <= DN_ArrayCountI(fn_0000_),
|
||||
"Max standard count is %d",
|
||||
STANDARD_FUNC_MAX_EAX + 1);
|
||||
DN_AssertF((DN_CAST(DN_ISize) EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DN_ArrayCountI(fn_8000_),
|
||||
"Max extended count is %zu",
|
||||
DN_CAST(DN_ISize) EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1);
|
||||
|
||||
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
|
||||
DN_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_0000_[eax] = DN_CPU_ID(args);
|
||||
}
|
||||
|
||||
for (int eax = EXTENDED_FUNC_BASE_EAX + 1, index = 1; eax <= EXTENDED_FUNC_MAX_EAX; eax++, index++) {
|
||||
DN_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_8000_[index] = DN_CPU_ID(args);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Query CPU vendor //////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DN_Memcpy(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU brand ///////////////////////////////////////////////////////////////////////
|
||||
if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) {
|
||||
DN_Memcpy(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 8, &fn_8000_[0x0002].reg.ecx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 12, &fn_8000_[0x0002].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_Memcpy(result.brand + 16, &fn_8000_[0x0003].reg.eax, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 20, &fn_8000_[0x0003].reg.ebx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 24, &fn_8000_[0x0003].reg.ecx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 28, &fn_8000_[0x0003].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_Memcpy(result.brand + 32, &fn_8000_[0x0004].reg.eax, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 36, &fn_8000_[0x0004].reg.ebx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 40, &fn_8000_[0x0004].reg.ecx, REGISTER_SIZE);
|
||||
DN_Memcpy(result.brand + 44, &fn_8000_[0x0004].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_Assert(result.brand[sizeof(result.brand) - 1] == 0);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU features //////////////////////////////////////////////////////////////////
|
||||
for (DN_USize ext_index = 0; ext_index < DN_CPUFeature_Count; ext_index++) {
|
||||
bool available = false;
|
||||
|
||||
// NOTE: Mask bits taken from various manuals
|
||||
// - AMD64 Architecture Programmer's Manual, Volumes 1-5
|
||||
// - https://en.wikipedia.org/wiki/CPUID#Calling_CPUID
|
||||
switch (DN_CAST(DN_CPUFeature) ext_index) {
|
||||
case DN_CPUFeature_3DNow: available = (fn_8000_[0x0001].reg.edx & (1 << 31)); break;
|
||||
case DN_CPUFeature_3DNowExt: available = (fn_8000_[0x0001].reg.edx & (1 << 30)); break;
|
||||
case DN_CPUFeature_ABM: available = (fn_8000_[0x0001].reg.ecx & (1 << 5)); break;
|
||||
case DN_CPUFeature_AES: available = (fn_0000_[0x0001].reg.ecx & (1 << 25)); break;
|
||||
case DN_CPUFeature_AVX: available = (fn_0000_[0x0001].reg.ecx & (1 << 28)); break;
|
||||
case DN_CPUFeature_AVX2: available = (fn_0000_[0x0007].reg.ebx & (1 << 0)); break;
|
||||
case DN_CPUFeature_AVX512F: available = (fn_0000_[0x0007].reg.ebx & (1 << 16)); break;
|
||||
case DN_CPUFeature_AVX512DQ: available = (fn_0000_[0x0007].reg.ebx & (1 << 17)); break;
|
||||
case DN_CPUFeature_AVX512IFMA: available = (fn_0000_[0x0007].reg.ebx & (1 << 21)); break;
|
||||
case DN_CPUFeature_AVX512PF: available = (fn_0000_[0x0007].reg.ebx & (1 << 26)); break;
|
||||
case DN_CPUFeature_AVX512ER: available = (fn_0000_[0x0007].reg.ebx & (1 << 27)); break;
|
||||
case DN_CPUFeature_AVX512CD: available = (fn_0000_[0x0007].reg.ebx & (1 << 28)); break;
|
||||
case DN_CPUFeature_AVX512BW: available = (fn_0000_[0x0007].reg.ebx & (1 << 30)); break;
|
||||
case DN_CPUFeature_AVX512VL: available = (fn_0000_[0x0007].reg.ebx & (1 << 31)); break;
|
||||
case DN_CPUFeature_AVX512VBMI: available = (fn_0000_[0x0007].reg.ecx & (1 << 1)); break;
|
||||
case DN_CPUFeature_AVX512VBMI2: available = (fn_0000_[0x0007].reg.ecx & (1 << 6)); break;
|
||||
case DN_CPUFeature_AVX512VNNI: available = (fn_0000_[0x0007].reg.ecx & (1 << 11)); break;
|
||||
case DN_CPUFeature_AVX512BITALG: available = (fn_0000_[0x0007].reg.ecx & (1 << 12)); break;
|
||||
case DN_CPUFeature_AVX512VPOPCNTDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 14)); break;
|
||||
case DN_CPUFeature_AVX5124VNNIW: available = (fn_0000_[0x0007].reg.edx & (1 << 2)); break;
|
||||
case DN_CPUFeature_AVX5124FMAPS: available = (fn_0000_[0x0007].reg.edx & (1 << 3)); break;
|
||||
case DN_CPUFeature_AVX512VP2INTERSECT: available = (fn_0000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_AVX512FP16: available = (fn_0000_[0x0007].reg.edx & (1 << 23)); break;
|
||||
case DN_CPUFeature_CLZERO: available = (fn_8000_[0x0008].reg.ebx & (1 << 0)); break;
|
||||
case DN_CPUFeature_CMPXCHG8B: available = (fn_0000_[0x0001].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_CMPXCHG16B: available = (fn_0000_[0x0001].reg.ecx & (1 << 13)); break;
|
||||
case DN_CPUFeature_F16C: available = (fn_0000_[0x0001].reg.ecx & (1 << 29)); break;
|
||||
case DN_CPUFeature_FMA: available = (fn_0000_[0x0001].reg.ecx & (1 << 12)); break;
|
||||
case DN_CPUFeature_FMA4: available = (fn_8000_[0x0001].reg.ecx & (1 << 16)); break;
|
||||
case DN_CPUFeature_FP128: available = (fn_8000_[0x001A].reg.eax & (1 << 0)); break;
|
||||
case DN_CPUFeature_FP256: available = (fn_8000_[0x001A].reg.eax & (1 << 2)); break;
|
||||
case DN_CPUFeature_FPU: available = (fn_0000_[0x0001].reg.edx & (1 << 0)); break;
|
||||
case DN_CPUFeature_MMX: available = (fn_0000_[0x0001].reg.edx & (1 << 23)); break;
|
||||
case DN_CPUFeature_MONITOR: available = (fn_0000_[0x0001].reg.ecx & (1 << 3)); break;
|
||||
case DN_CPUFeature_MOVBE: available = (fn_0000_[0x0001].reg.ecx & (1 << 22)); break;
|
||||
case DN_CPUFeature_MOVU: available = (fn_8000_[0x001A].reg.eax & (1 << 1)); break;
|
||||
case DN_CPUFeature_MmxExt: available = (fn_8000_[0x0001].reg.edx & (1 << 22)); break;
|
||||
case DN_CPUFeature_PCLMULQDQ: available = (fn_0000_[0x0001].reg.ecx & (1 << 1)); break;
|
||||
case DN_CPUFeature_POPCNT: available = (fn_0000_[0x0001].reg.ecx & (1 << 23)); break;
|
||||
case DN_CPUFeature_RDRAND: available = (fn_0000_[0x0001].reg.ecx & (1 << 30)); break;
|
||||
case DN_CPUFeature_RDSEED: available = (fn_0000_[0x0007].reg.ebx & (1 << 18)); break;
|
||||
case DN_CPUFeature_RDTSCP: available = (fn_8000_[0x0001].reg.edx & (1 << 27)); break;
|
||||
case DN_CPUFeature_SHA: available = (fn_0000_[0x0007].reg.ebx & (1 << 29)); break;
|
||||
case DN_CPUFeature_SSE: available = (fn_0000_[0x0001].reg.edx & (1 << 25)); break;
|
||||
case DN_CPUFeature_SSE2: available = (fn_0000_[0x0001].reg.edx & (1 << 26)); break;
|
||||
case DN_CPUFeature_SSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 0)); break;
|
||||
case DN_CPUFeature_SSE41: available = (fn_0000_[0x0001].reg.ecx & (1 << 19)); break;
|
||||
case DN_CPUFeature_SSE42: available = (fn_0000_[0x0001].reg.ecx & (1 << 20)); break;
|
||||
case DN_CPUFeature_SSE4A: available = (fn_8000_[0x0001].reg.ecx & (1 << 6)); break;
|
||||
case DN_CPUFeature_SSSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 9)); break;
|
||||
case DN_CPUFeature_TSC: available = (fn_0000_[0x0001].reg.edx & (1 << 4)); break;
|
||||
case DN_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break;
|
||||
case DN_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break;
|
||||
case DN_CPUFeature_Count: DN_InvalidCodePath; break;
|
||||
}
|
||||
|
||||
if (available)
|
||||
DN_CPU_SetFeature(&result, DN_CAST(DN_CPUFeature) ext_index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
|
||||
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex)
|
||||
{
|
||||
unsigned int ticket = DN_Atomic_AddU32(&mutex->ticket, 1);
|
||||
DN_TicketMutex_BeginTicket(mutex, ticket);
|
||||
}
|
||||
|
||||
DN_API void DN_TicketMutex_End(DN_TicketMutex *mutex)
|
||||
{
|
||||
DN_Atomic_AddU32(&mutex->serving, 1);
|
||||
}
|
||||
|
||||
DN_API DN_UInt DN_TicketMutex_MakeTicket(DN_TicketMutex *mutex)
|
||||
{
|
||||
DN_UInt result = DN_Atomic_AddU32(&mutex->ticket, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_TicketMutex_BeginTicket(DN_TicketMutex const *mutex, DN_UInt ticket)
|
||||
{
|
||||
DN_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();
|
||||
}
|
||||
}
|
||||
|
||||
DN_API bool DN_TicketMutex_CanLock(DN_TicketMutex const *mutex, DN_UInt ticket)
|
||||
{
|
||||
bool result = (ticket == mutex->serving);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#if !defined(DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED)
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: DN_Bit ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_Bit_UnsetInplace(DN_USize *flags, DN_USize bitfield)
|
||||
{
|
||||
*flags = (*flags & ~bitfield);
|
||||
}
|
||||
|
||||
DN_API void DN_Bit_SetInplace(DN_USize *flags, DN_USize bitfield)
|
||||
{
|
||||
*flags = (*flags | bitfield);
|
||||
}
|
||||
|
||||
DN_API bool DN_Bit_IsSet(DN_USize bits, DN_USize bits_to_set)
|
||||
{
|
||||
auto result = DN_CAST(bool)((bits & bits_to_set) == bits_to_set);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_Bit_IsNotSet(DN_USize bits, DN_USize bits_to_check)
|
||||
{
|
||||
auto result = !DN_Bit_IsSet(bits, bits_to_check);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_Safe ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_I64 DN_Safe_AddI64(int64_t a, int64_t b)
|
||||
{
|
||||
DN_I64 result = DN_CheckF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I64 DN_Safe_MulI64(int64_t a, int64_t b)
|
||||
{
|
||||
DN_I64 result = DN_CheckF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_Safe_AddU64(DN_U64 a, DN_U64 b)
|
||||
{
|
||||
DN_U64 result = DN_CheckF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_Safe_SubU64(DN_U64 a, DN_U64 b)
|
||||
{
|
||||
DN_U64 result = DN_CheckF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_Safe_MulU64(DN_U64 a, DN_U64 b)
|
||||
{
|
||||
DN_U64 result = DN_CheckF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_Safe_SubU32(DN_U32 a, DN_U32 b)
|
||||
{
|
||||
DN_U32 result = DN_CheckF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_SaturateCastUSizeToI* ////////////////////////////////////////////////////////////
|
||||
// INT*_MAX literals will be promoted to the type of uintmax_t as uintmax_t is
|
||||
// the highest possible rank (unsigned > signed).
|
||||
DN_API int DN_SaturateCastUSizeToInt(DN_USize val)
|
||||
{
|
||||
int result = DN_Check(DN_CAST(uintmax_t) val <= INT_MAX) ? DN_CAST(int) val : INT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int8_t DN_SaturateCastUSizeToI8(DN_USize val)
|
||||
{
|
||||
int8_t result = DN_Check(DN_CAST(uintmax_t) val <= INT8_MAX) ? DN_CAST(int8_t) val : INT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I16 DN_SaturateCastUSizeToI16(DN_USize val)
|
||||
{
|
||||
DN_I16 result = DN_Check(DN_CAST(uintmax_t) val <= INT16_MAX) ? DN_CAST(DN_I16) val : INT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I32 DN_SaturateCastUSizeToI32(DN_USize val)
|
||||
{
|
||||
DN_I32 result = DN_Check(DN_CAST(uintmax_t) val <= INT32_MAX) ? DN_CAST(DN_I32) val : INT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int64_t DN_SaturateCastUSizeToI64(DN_USize val)
|
||||
{
|
||||
int64_t result = DN_Check(DN_CAST(uintmax_t) val <= INT64_MAX) ? DN_CAST(int64_t) val : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_SaturateCastUSizeToU* ////////////////////////////////////////////////////////////
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DN_API DN_U8 DN_SaturateCastUSizeToU8(DN_USize val)
|
||||
{
|
||||
DN_U8 result = DN_Check(val <= UINT8_MAX) ? DN_CAST(DN_U8) val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U16 DN_SaturateCastUSizeToU16(DN_USize val)
|
||||
{
|
||||
DN_U16 result = DN_Check(val <= UINT16_MAX) ? DN_CAST(DN_U16) val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_SaturateCastUSizeToU32(DN_USize val)
|
||||
{
|
||||
DN_U32 result = DN_Check(val <= UINT32_MAX) ? DN_CAST(DN_U32) val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_SaturateCastUSizeToU64(DN_USize val)
|
||||
{
|
||||
DN_U64 result = DN_Check(DN_CAST(DN_U64) val <= UINT64_MAX) ? DN_CAST(DN_U64) val : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_SaturateCastU64To* ///////////////////////////////////////////////////////////////
|
||||
DN_API int DN_SaturateCastU64ToInt(DN_U64 val)
|
||||
{
|
||||
int result = DN_Check(val <= INT_MAX) ? DN_CAST(int) val : INT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int8_t DN_SaturateCastU64ToI8(DN_U64 val)
|
||||
{
|
||||
int8_t result = DN_Check(val <= INT8_MAX) ? DN_CAST(int8_t) val : INT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I16 DN_SaturateCastU64ToI16(DN_U64 val)
|
||||
{
|
||||
DN_I16 result = DN_Check(val <= INT16_MAX) ? DN_CAST(DN_I16) val : INT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I32 DN_SaturateCastU64ToI32(DN_U64 val)
|
||||
{
|
||||
DN_I32 result = DN_Check(val <= INT32_MAX) ? DN_CAST(DN_I32) val : INT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int64_t DN_SaturateCastU64ToI64(DN_U64 val)
|
||||
{
|
||||
int64_t result = DN_Check(val <= INT64_MAX) ? DN_CAST(int64_t) val : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DN_API unsigned int DN_SaturateCastU64ToUInt(DN_U64 val)
|
||||
{
|
||||
unsigned int result = DN_Check(val <= UINT8_MAX) ? DN_CAST(unsigned int) val : UINT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U8 DN_SaturateCastU64ToU8(DN_U64 val)
|
||||
{
|
||||
DN_U8 result = DN_Check(val <= UINT8_MAX) ? DN_CAST(DN_U8) val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U16 DN_SaturateCastU64ToU16(DN_U64 val)
|
||||
{
|
||||
DN_U16 result = DN_Check(val <= UINT16_MAX) ? DN_CAST(DN_U16) val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_SaturateCastU64ToU32(DN_U64 val)
|
||||
{
|
||||
DN_U32 result = DN_Check(val <= UINT32_MAX) ? DN_CAST(DN_U32) val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_SaturateCastISizeToI* ////////////////////////////////////////////////////////////
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DN_API int DN_SaturateCastISizeToInt(DN_ISize val)
|
||||
{
|
||||
DN_Assert(val >= INT_MIN && val <= INT_MAX);
|
||||
int result = DN_CAST(int) DN_Clamp(val, INT_MIN, INT_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int8_t DN_SaturateCastISizeToI8(DN_ISize val)
|
||||
{
|
||||
DN_Assert(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I16 DN_SaturateCastISizeToI16(DN_ISize val)
|
||||
{
|
||||
DN_Assert(val >= INT16_MIN && val <= INT16_MAX);
|
||||
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I32 DN_SaturateCastISizeToI32(DN_ISize val)
|
||||
{
|
||||
DN_Assert(val >= INT32_MIN && val <= INT32_MAX);
|
||||
DN_I32 result = DN_CAST(DN_I32) DN_Clamp(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int64_t DN_SaturateCastISizeToI64(DN_ISize val)
|
||||
{
|
||||
DN_Assert(DN_CAST(int64_t) val >= INT64_MIN && DN_CAST(int64_t) val <= INT64_MAX);
|
||||
int64_t result = DN_CAST(int64_t) DN_Clamp(DN_CAST(int64_t) val, INT64_MIN, INT64_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_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.
|
||||
DN_API unsigned int DN_SaturateCastISizeToUInt(DN_ISize val)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT_MAX))
|
||||
result = DN_CAST(unsigned int) val;
|
||||
else
|
||||
result = UINT_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U8 DN_SaturateCastISizeToU8(DN_ISize val)
|
||||
{
|
||||
DN_U8 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
|
||||
result = DN_CAST(DN_U8) val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U16 DN_SaturateCastISizeToU16(DN_ISize val)
|
||||
{
|
||||
DN_U16 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
|
||||
result = DN_CAST(DN_U16) val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_SaturateCastISizeToU32(DN_ISize val)
|
||||
{
|
||||
DN_U32 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT32_MAX))
|
||||
result = DN_CAST(DN_U32) val;
|
||||
else
|
||||
result = UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_SaturateCastISizeToU64(DN_ISize val)
|
||||
{
|
||||
DN_U64 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT64_MAX))
|
||||
result = DN_CAST(DN_U64) val;
|
||||
else
|
||||
result = UINT64_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_SaturateCastI64To* ///////////////////////////////////////////////////////////////
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DN_API DN_ISize DN_SaturateCastI64ToISize(int64_t val)
|
||||
{
|
||||
DN_Check(val >= DN_ISIZE_MIN && val <= DN_ISIZE_MAX);
|
||||
DN_ISize result = DN_CAST(int64_t) DN_Clamp(val, DN_ISIZE_MIN, DN_ISIZE_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int8_t DN_SaturateCastI64ToI8(int64_t val)
|
||||
{
|
||||
DN_Check(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I16 DN_SaturateCastI64ToI16(int64_t val)
|
||||
{
|
||||
DN_Check(val >= INT16_MIN && val <= INT16_MAX);
|
||||
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I32 DN_SaturateCastI64ToI32(int64_t val)
|
||||
{
|
||||
DN_Check(val >= INT32_MIN && val <= INT32_MAX);
|
||||
DN_I32 result = DN_CAST(DN_I32) DN_Clamp(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API unsigned int DN_SaturateCastI64ToUInt(int64_t val)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT_MAX))
|
||||
result = DN_CAST(unsigned int) val;
|
||||
else
|
||||
result = UINT_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val)
|
||||
{
|
||||
DN_USize result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= DN_USIZE_MAX))
|
||||
result = DN_CAST(DN_USize) val;
|
||||
else
|
||||
result = DN_USIZE_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val)
|
||||
{
|
||||
DN_U8 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
|
||||
result = DN_CAST(DN_U8) val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val)
|
||||
{
|
||||
DN_U16 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
|
||||
result = DN_CAST(DN_U16) val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val)
|
||||
{
|
||||
DN_U32 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT32_MAX))
|
||||
result = DN_CAST(DN_U32) val;
|
||||
else
|
||||
result = UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val)
|
||||
{
|
||||
DN_U64 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT64_MAX))
|
||||
result = DN_CAST(DN_U64) val;
|
||||
else
|
||||
result = UINT64_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int8_t DN_SaturateCastIntToI8(int val)
|
||||
{
|
||||
DN_Check(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_I16 DN_SaturateCastIntToI16(int val)
|
||||
{
|
||||
DN_Check(val >= INT16_MIN && val <= INT16_MAX);
|
||||
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U8 DN_SaturateCastIntToU8(int val)
|
||||
{
|
||||
DN_U8 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
|
||||
result = DN_CAST(DN_U8) val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U16 DN_SaturateCastIntToU16(int val)
|
||||
{
|
||||
DN_U16 result = 0;
|
||||
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
|
||||
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
|
||||
result = DN_CAST(DN_U16) val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U32 DN_SaturateCastIntToU32(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(DN_U32), "Sanity check to allow simplifying of casting");
|
||||
DN_U32 result = 0;
|
||||
if (DN_Check(val >= 0))
|
||||
result = DN_CAST(DN_U32) val;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_SaturateCastIntToU64(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(DN_U64), "Sanity check to allow simplifying of casting");
|
||||
DN_U64 result = 0;
|
||||
if (DN_Check(val >= 0))
|
||||
result = DN_CAST(DN_U64) val;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_Asan ////////////////////////////////////////////////////////////////////////// ////////
|
||||
static_assert(DN_IsPowerOfTwoAligned(DN_ASAN_POISON_GUARD_SIZE, DN_ASAN_POISON_ALIGNMENT),
|
||||
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
|
||||
"requirement (8 bytes)");
|
||||
|
||||
DN_API void DN_ASAN_PoisonMemoryRegion(void const volatile *ptr, DN_USize size)
|
||||
{
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
DN_AssertF(DN_IsPowerOfTwoAligned(ptr, 8),
|
||||
"Poisoning requires the pointer to be aligned on an 8 byte boundary");
|
||||
|
||||
__asan_poison_memory_region(ptr, size);
|
||||
if (DN_ASAN_VET_POISON) {
|
||||
DN_HardAssert(__asan_address_is_poisoned(ptr));
|
||||
DN_HardAssert(__asan_address_is_poisoned((char *)ptr + (size - 1)));
|
||||
}
|
||||
#else
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, DN_USize size)
|
||||
{
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
__asan_unpoison_memory_region(ptr, size);
|
||||
if (DN_ASAN_VET_POISON)
|
||||
DN_HardAssert(__asan_region_is_poisoned((void *)ptr, size) == 0);
|
||||
#else
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
#endif
|
||||
}
|
613
Base/dn_base.h
Normal file
613
Base/dn_base.h
Normal file
@ -0,0 +1,613 @@
|
||||
#if !defined(DN_BASE_H)
|
||||
#define DN_BASE_H
|
||||
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_Stringify(x) #x
|
||||
#define DN_TokenCombine2(x, y) x ## y
|
||||
#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y)
|
||||
|
||||
#include <stdarg.h> // va_list
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h> // PRIu64...
|
||||
|
||||
#if !defined(DN_OS_WIN32)
|
||||
#include <stdlib.h> // exit()
|
||||
#endif
|
||||
|
||||
#define DN_ForIndexU(index, size) for (DN_USize index = 0; index < size; index++)
|
||||
#define DN_ForIndexI(index, size) for (DN_ISize index = 0; index < size; index++)
|
||||
#define DN_ForItSize(it, T, array, size) for (struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data++)
|
||||
#define DN_ForIt(it, T, array) for (struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data++)
|
||||
#define DN_ForItCArray(it, T, array) for (struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data++)
|
||||
|
||||
#define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
|
||||
#define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
|
||||
#define DN_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
|
||||
#define DN_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
|
||||
|
||||
// NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_Memcpy) || !defined(DN_Memset) || !defined(DN_Memcmp) || !defined(DN_Memmove)
|
||||
#include <string.h>
|
||||
#if !defined(DN_Memcpy)
|
||||
#define DN_Memcpy(dest, src, count) memcpy((dest), (src), (count))
|
||||
#endif
|
||||
#if !defined(DN_Memset)
|
||||
#define DN_Memset(dest, value, count) memset((dest), (value), (count))
|
||||
#endif
|
||||
#if !defined(DN_Memcmp)
|
||||
#define DN_Memcmp(lhs, rhs, count) memcmp((lhs), (rhs), (count))
|
||||
#endif
|
||||
#if !defined(DN_Memmove)
|
||||
#define DN_Memmove(dest, src, count) memmove((dest), (src), (count))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_SqrtF32) || !defined(DN_SinF32) || !defined(DN_CosF32) || !defined(DN_TanF32)
|
||||
#include <math.h>
|
||||
#if !defined(DN_SqrtF32)
|
||||
#define DN_SqrtF32(val) sqrtf(val)
|
||||
#endif
|
||||
#if !defined(DN_SinF32)
|
||||
#define DN_SinF32(val) sinf(val)
|
||||
#endif
|
||||
#if !defined(DN_CosF32)
|
||||
#define DN_CosF32(val) cosf(val)
|
||||
#endif
|
||||
#if !defined(DN_TanF32)
|
||||
#define DN_TanF32(val) tanf(val)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_PiF32 3.14159265359f
|
||||
|
||||
#define DN_DegreesToRadians(degrees) ((degrees) * (DN_PI / 180.0f))
|
||||
#define DN_RadiansToDegrees(radians) ((radians) * (180.f * DN_PI))
|
||||
|
||||
#define DN_Abs(val) (((val) < 0) ? (-(val)) : (val))
|
||||
#define DN_Max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define DN_Min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define DN_Clamp(val, lo, hi) DN_Max(DN_Min(val, hi), lo)
|
||||
#define DN_Squared(val) ((val) * (val))
|
||||
|
||||
#define DN_Swap(a, b) \
|
||||
do { \
|
||||
auto temp = a; \
|
||||
a = b; \
|
||||
b = temp; \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_SizeOfI(val) DN_CAST(ptrdiff_t)sizeof(val)
|
||||
#define DN_ArrayCountU(array) (sizeof(array)/(sizeof((array)[0])))
|
||||
#define DN_ArrayCountI(array) (DN_ISize)DN_ArrayCountU(array)
|
||||
#define DN_CharCountU(string) (sizeof(string) - 1)
|
||||
|
||||
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_Bytes(val) ((DN_U64)val)
|
||||
#define DN_Kilobytes(val) ((DN_U64)1024 * DN_Bytes(val))
|
||||
#define DN_Megabytes(val) ((DN_U64)1024 * DN_Kilobytes(val))
|
||||
#define DN_Gigabytes(val) ((DN_U64)1024 * DN_Megabytes(val))
|
||||
|
||||
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_SecondsToMs(val) ((val) * 1000)
|
||||
#define DN_MinutesToSec(val) ((val) * 60ULL)
|
||||
#define DN_HoursToSec(val) (DN_MinutesToSec(val) * 60ULL)
|
||||
#define DN_DaysToSec(val) (DN_HoursToSec(val) * 24ULL)
|
||||
#define DN_WeeksToSec(val) (DN_DaysToSec(val) * 7ULL)
|
||||
#define DN_YearsToSec(val) (DN_WeeksToSec(val) * 52ULL)
|
||||
|
||||
// NOTE: Debug Break ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_DebugBreak)
|
||||
#if defined(NDEBUG)
|
||||
#define DN_DebugBreak
|
||||
#else
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_DebugBreak __debugbreak()
|
||||
#elif DN_HAS_BUILTIN(__builtin_debugtrap)
|
||||
#define DN_DebugBreak __builtin_debugtrap()
|
||||
#elif DN_HAS_BUILTIN(__builtin_trap) || defined(DN_COMPILER_GCC)
|
||||
#define DN_DebugBreak __builtin_trap()
|
||||
#else
|
||||
#include <signal.h>
|
||||
#if defined(SIGTRAP)
|
||||
#define DN_DebugBreak raise(SIGTRAP)
|
||||
#else
|
||||
#define DN_DebugBreak raise(SIGABRT)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Byte swaps ////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_ByteSwap64(u64) (((((u64) >> 56) & 0xFF) << 0) | \
|
||||
((((u64) >> 48) & 0xFF) << 8) | \
|
||||
((((u64) >> 40) & 0xFF) << 16) | \
|
||||
((((u64) >> 32) & 0xFF) << 24) | \
|
||||
((((u64) >> 24) & 0xFF) << 32) | \
|
||||
((((u64) >> 16) & 0xFF) << 40) | \
|
||||
((((u64) >> 8) & 0xFF) << 48) | \
|
||||
((((u64) >> 0) & 0xFF) << 56))
|
||||
|
||||
// NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
template <typename Procedure>
|
||||
struct DN_Defer
|
||||
{
|
||||
Procedure proc;
|
||||
DN_Defer(Procedure p) : proc(p) {}
|
||||
~DN_Defer() { proc(); }
|
||||
};
|
||||
|
||||
struct DN_DeferHelper
|
||||
{
|
||||
template <typename Lambda>
|
||||
DN_Defer<Lambda> operator+(Lambda lambda) { return DN_Defer<Lambda>(lambda); };
|
||||
};
|
||||
|
||||
#define DN_UniqueName(prefix) DN_TokenCombine(prefix, __LINE__)
|
||||
#define DN_DEFER const auto DN_UniqueName(defer_lambda_) = DN_DeferHelper() + [&]()
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#define DN_DEFER_LOOP(begin, end) \
|
||||
for (bool DN_UniqueName(once) = (begin, true); \
|
||||
DN_UniqueName(once); \
|
||||
end, DN_UniqueName(once) = false)
|
||||
|
||||
// NOTE: Types /////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef intptr_t DN_ISize;
|
||||
typedef uintptr_t DN_USize;
|
||||
|
||||
typedef int8_t DN_I8;
|
||||
typedef int16_t DN_I16;
|
||||
typedef int32_t DN_I32;
|
||||
typedef int64_t DN_I64;
|
||||
|
||||
typedef uint8_t DN_U8;
|
||||
typedef uint16_t DN_U16;
|
||||
typedef uint32_t DN_U32;
|
||||
typedef uint64_t DN_U64;
|
||||
|
||||
typedef float DN_F32;
|
||||
typedef double DN_F64;
|
||||
typedef unsigned int DN_UInt;
|
||||
typedef DN_I32 DN_B32;
|
||||
|
||||
#define DN_F32_MAX 3.402823466e+38F
|
||||
#define DN_F32_MIN 1.175494351e-38F
|
||||
#define DN_F64_MAX 1.7976931348623158e+308
|
||||
#define DN_F64_MIN 2.2250738585072014e-308
|
||||
#define DN_USIZE_MAX UINTPTR_MAX
|
||||
#define DN_ISIZE_MAX INTPTR_MAX
|
||||
#define DN_ISIZE_MIN INTPTR_MIN
|
||||
|
||||
enum DN_ZeroMem
|
||||
{
|
||||
DN_ZeroMem_No, // Memory can be handed out without zero-ing it out
|
||||
DN_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee
|
||||
};
|
||||
|
||||
struct DN_Str8
|
||||
{
|
||||
char *data; // The bytes of the string
|
||||
DN_USize size; // The number of bytes in the string
|
||||
|
||||
char const *begin() const { return data; }
|
||||
char const *end() const { return data + size; }
|
||||
char *begin() { return data; }
|
||||
char *end() { return data + size; }
|
||||
};
|
||||
|
||||
struct DN_Str16 // A pointer and length style string that holds slices to UTF16 bytes.
|
||||
{
|
||||
wchar_t *data; // The UTF16 bytes of the string
|
||||
DN_USize size; // The number of characters in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
wchar_t const *begin() const { return data; } // Const begin iterator for range-for loops
|
||||
wchar_t const *end() const { return data + size; } // Const end iterator for range-for loops
|
||||
wchar_t *begin() { return data; } // Begin iterator for range-for loops
|
||||
wchar_t *end() { return data + size; } // End iterator for range-for loops
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DN_Slice // A pointer and length container of data
|
||||
{
|
||||
T *data;
|
||||
DN_USize size;
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end() { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end() const { return data + size; }
|
||||
};
|
||||
|
||||
// NOTE: DN_CallSite ///////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_CallSite
|
||||
{
|
||||
DN_Str8 file;
|
||||
DN_Str8 function;
|
||||
DN_U32 line;
|
||||
};
|
||||
|
||||
#define DN_CALL_SITE \
|
||||
DN_CallSite \
|
||||
{ \
|
||||
DN_STR8(__FILE__), DN_STR8(__func__), __LINE__ \
|
||||
}
|
||||
|
||||
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Atomic_Add/Exchange return the previous value store in the target
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#include <intrin.h>
|
||||
#define DN_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
|
||||
#define DN_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
|
||||
#define DN_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
|
||||
#define DN_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
|
||||
#define DN_Atomic_SubU32(target, value) DN_Atomic_AddU32(DN_CAST(long volatile *) target, (long)-value)
|
||||
#define DN_Atomic_SubU64(target, value) DN_Atomic_AddU64(target, (DN_U64) - value)
|
||||
|
||||
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
|
||||
|
||||
#define DN_CPU_TSC() __rdtsc()
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence \
|
||||
_ReadBarrier(); \
|
||||
_mm_lfence()
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence \
|
||||
_WriteBarrier(); \
|
||||
_mm_sfence()
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
#if defined(__ANDROID__)
|
||||
#elif defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emmintrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#define DN_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
|
||||
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
|
||||
#if defined(DN_COMPILER_GCC)
|
||||
#define DN_CPU_TSC() __rdtsc()
|
||||
#else
|
||||
#define DN_CPU_TSC() __builtin_readcyclecounter()
|
||||
#endif
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence
|
||||
#else
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory")
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory")
|
||||
#endif
|
||||
#else
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
|
||||
#if !defined(DN_PLATFORM_ARM64)
|
||||
struct DN_CPURegisters
|
||||
{
|
||||
int eax;
|
||||
int ebx;
|
||||
int ecx;
|
||||
int edx;
|
||||
};
|
||||
|
||||
union DN_CPUIDResult
|
||||
{
|
||||
DN_CPURegisters reg;
|
||||
int values[4];
|
||||
};
|
||||
|
||||
struct DN_CPUIDArgs
|
||||
{
|
||||
int eax;
|
||||
int ecx;
|
||||
};
|
||||
|
||||
#define DN_CPU_FEAT_XMACRO \
|
||||
DN_CPU_FEAT_XENTRY(3DNow) \
|
||||
DN_CPU_FEAT_XENTRY(3DNowExt) \
|
||||
DN_CPU_FEAT_XENTRY(ABM) \
|
||||
DN_CPU_FEAT_XENTRY(AES) \
|
||||
DN_CPU_FEAT_XENTRY(AVX) \
|
||||
DN_CPU_FEAT_XENTRY(AVX2) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512F) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512DQ) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512IFMA) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512PF) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512ER) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512CD) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512BW) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VL) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VBMI) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VBMI2) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VNNI) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512BITALG) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VPOPCNTDQ) \
|
||||
DN_CPU_FEAT_XENTRY(AVX5124VNNIW) \
|
||||
DN_CPU_FEAT_XENTRY(AVX5124FMAPS) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VP2INTERSECT) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512FP16) \
|
||||
DN_CPU_FEAT_XENTRY(CLZERO) \
|
||||
DN_CPU_FEAT_XENTRY(CMPXCHG8B) \
|
||||
DN_CPU_FEAT_XENTRY(CMPXCHG16B) \
|
||||
DN_CPU_FEAT_XENTRY(F16C) \
|
||||
DN_CPU_FEAT_XENTRY(FMA) \
|
||||
DN_CPU_FEAT_XENTRY(FMA4) \
|
||||
DN_CPU_FEAT_XENTRY(FP128) \
|
||||
DN_CPU_FEAT_XENTRY(FP256) \
|
||||
DN_CPU_FEAT_XENTRY(FPU) \
|
||||
DN_CPU_FEAT_XENTRY(MMX) \
|
||||
DN_CPU_FEAT_XENTRY(MONITOR) \
|
||||
DN_CPU_FEAT_XENTRY(MOVBE) \
|
||||
DN_CPU_FEAT_XENTRY(MOVU) \
|
||||
DN_CPU_FEAT_XENTRY(MmxExt) \
|
||||
DN_CPU_FEAT_XENTRY(PCLMULQDQ) \
|
||||
DN_CPU_FEAT_XENTRY(POPCNT) \
|
||||
DN_CPU_FEAT_XENTRY(RDRAND) \
|
||||
DN_CPU_FEAT_XENTRY(RDSEED) \
|
||||
DN_CPU_FEAT_XENTRY(RDTSCP) \
|
||||
DN_CPU_FEAT_XENTRY(SHA) \
|
||||
DN_CPU_FEAT_XENTRY(SSE) \
|
||||
DN_CPU_FEAT_XENTRY(SSE2) \
|
||||
DN_CPU_FEAT_XENTRY(SSE3) \
|
||||
DN_CPU_FEAT_XENTRY(SSE41) \
|
||||
DN_CPU_FEAT_XENTRY(SSE42) \
|
||||
DN_CPU_FEAT_XENTRY(SSE4A) \
|
||||
DN_CPU_FEAT_XENTRY(SSSE3) \
|
||||
DN_CPU_FEAT_XENTRY(TSC) \
|
||||
DN_CPU_FEAT_XENTRY(TscInvariant) \
|
||||
DN_CPU_FEAT_XENTRY(VAES) \
|
||||
DN_CPU_FEAT_XENTRY(VPCMULQDQ)
|
||||
|
||||
enum DN_CPUFeature
|
||||
{
|
||||
#define DN_CPU_FEAT_XENTRY(label) DN_CPUFeature_##label,
|
||||
DN_CPU_FEAT_XMACRO
|
||||
#undef DN_CPU_FEAT_XENTRY
|
||||
DN_CPUFeature_Count,
|
||||
};
|
||||
|
||||
struct DN_CPUFeatureDecl
|
||||
{
|
||||
DN_CPUFeature value;
|
||||
DN_Str8 label;
|
||||
};
|
||||
|
||||
struct DN_CPUFeatureQuery
|
||||
{
|
||||
DN_CPUFeature feature;
|
||||
bool available;
|
||||
};
|
||||
|
||||
struct DN_CPUReport
|
||||
{
|
||||
char vendor[4 /*bytes*/ * 3 /*EDX, ECX, EBX*/ + 1 /*null*/];
|
||||
char brand[48];
|
||||
DN_U64 features[(DN_CPUFeature_Count / (sizeof(DN_U64) * 8)) + 1];
|
||||
};
|
||||
|
||||
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
||||
#endif // DN_PLATFORM_ARM64
|
||||
|
||||
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_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
|
||||
};
|
||||
|
||||
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
|
||||
DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64 (DN_U64 volatile *target, DN_U64 value);
|
||||
DN_FORCE_INLINE long DN_Atomic_SetValue32 (long volatile *target, long value);
|
||||
|
||||
#if !defined (DN_PLATFORM_ARM64)
|
||||
DN_API DN_CPUIDResult DN_CPU_ID (DN_CPUIDArgs args);
|
||||
DN_API DN_USize DN_CPU_HasFeatureArray (DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size);
|
||||
DN_API bool DN_CPU_HasFeature (DN_CPUReport const *report, DN_CPUFeature feature);
|
||||
DN_API bool DN_CPU_HasAllFeatures (DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size);
|
||||
template <DN_USize N>
|
||||
bool DN_CPU_HasAllFeaturesCArray(DN_CPUReport const *report, DN_CPUFeature const (&features)[N]);
|
||||
DN_API void DN_CPU_SetFeature (DN_CPUReport *report, DN_CPUFeature feature);
|
||||
DN_API DN_CPUReport DN_CPU_Report ();
|
||||
#endif
|
||||
|
||||
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_TicketMutex_Begin (DN_TicketMutex *mutex);
|
||||
DN_API void DN_TicketMutex_End (DN_TicketMutex *mutex);
|
||||
DN_API DN_UInt DN_TicketMutex_MakeTicket (DN_TicketMutex *mutex);
|
||||
DN_API void DN_TicketMutex_BeginTicket(DN_TicketMutex const *mutex, DN_UInt ticket);
|
||||
DN_API bool DN_TicketMutex_CanLock (DN_TicketMutex const *mutex, DN_UInt ticket);
|
||||
|
||||
// NOTE: DN_DLList /////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_DLList_Init(list) \
|
||||
(list)->next = (list)->prev = (list)
|
||||
|
||||
#define DN_DLList_IsSentinel(list, item) ((list) == (item))
|
||||
|
||||
#define DN_DLList_InitArena(list, T, arena) \
|
||||
do { \
|
||||
(list) = DN_Arena_New(arena, T, DN_ZeroMem_Yes); \
|
||||
DN_DLList_Init(list); \
|
||||
} while (0)
|
||||
|
||||
#define DN_DLList_InitPool(list, T, pool) \
|
||||
do { \
|
||||
(list) = DN_Pool_New(pool, T); \
|
||||
DN_DLList_Init(list); \
|
||||
} while (0)
|
||||
|
||||
#define DN_DLList_Detach(item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
(item)->prev->next = (item)->next; \
|
||||
(item)->next->prev = (item)->prev; \
|
||||
(item)->next = nullptr; \
|
||||
(item)->prev = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_DLList_Dequeue(list, dest_ptr) \
|
||||
if (DN_DLList_HasItems(list)) { \
|
||||
dest_ptr = (list)->next; \
|
||||
DN_DLList_Detach(dest_ptr); \
|
||||
}
|
||||
|
||||
#define DN_DLList_Append(list, item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
if ((item)->next) \
|
||||
DN_DLList_Detach(item); \
|
||||
(item)->next = (list)->next; \
|
||||
(item)->prev = (list); \
|
||||
(item)->next->prev = (item); \
|
||||
(item)->prev->next = (item); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_DLList_Prepend(list, item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
if ((item)->next) \
|
||||
DN_DLList_Detach(item); \
|
||||
(item)->next = (list); \
|
||||
(item)->prev = (list)->prev; \
|
||||
(item)->next->prev = (item); \
|
||||
(item)->prev->next = (item); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_DLList_IsEmpty(list) \
|
||||
(!(list) || ((list) == (list)->next))
|
||||
|
||||
#define DN_DLList_IsInit(list) \
|
||||
((list)->next && (list)->prev)
|
||||
|
||||
#define DN_DLList_HasItems(list) \
|
||||
((list) && ((list) != (list)->next))
|
||||
|
||||
#define DN_DLList_ForEach(it_name, list) \
|
||||
auto *it_name = (list)->next; \
|
||||
(it_name) != (list); \
|
||||
(it_name) = (it_name)->next
|
||||
|
||||
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
|
||||
DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64(DN_U64 volatile *target, DN_U64 value)
|
||||
{
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
__int64 result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (DN_Atomic_CompareExchange64(target, value, result) != result);
|
||||
return DN_CAST(DN_U64) result;
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
DN_U64 result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_FORCE_INLINE long DN_Atomic_SetValue32(long volatile *target, long value)
|
||||
{
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
long result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (DN_Atomic_CompareExchange32(target, value, result) != result);
|
||||
return result;
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
long result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
bool DN_CPU_HasAllFeaturesCArray(DN_CPUReport const *report, DN_CPUFeature const (&features)[N])
|
||||
{
|
||||
bool result = DN_CPU_HasAllFeatures(report, features, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_Bit ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_Bit_UnsetInplace (DN_USize *flags, DN_USize bitfield);
|
||||
DN_API void DN_Bit_SetInplace (DN_USize *flags, DN_USize bitfield);
|
||||
DN_API bool DN_Bit_IsSet (DN_USize bits, DN_USize bits_to_set);
|
||||
DN_API bool DN_Bit_IsNotSet (DN_USize bits, DN_USize bits_to_check);
|
||||
#define DN_Bit_ClearNextLSB(value) (value) & ((value) - 1)
|
||||
|
||||
// NOTE: DN_Safe ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_I64 DN_Safe_AddI64 (DN_I64 a, DN_I64 b);
|
||||
DN_API DN_I64 DN_Safe_MulI64 (DN_I64 a, DN_I64 b);
|
||||
|
||||
DN_API DN_U64 DN_Safe_AddU64 (DN_U64 a, DN_U64 b);
|
||||
DN_API DN_U64 DN_Safe_MulU64 (DN_U64 a, DN_U64 b);
|
||||
|
||||
DN_API DN_U64 DN_Safe_SubU64 (DN_U64 a, DN_U64 b);
|
||||
DN_API DN_U32 DN_Safe_SubU32 (DN_U32 a, DN_U32 b);
|
||||
|
||||
DN_API int DN_SaturateCastUSizeToInt (DN_USize val);
|
||||
DN_API DN_I8 DN_SaturateCastUSizeToI8 (DN_USize val);
|
||||
DN_API DN_I16 DN_SaturateCastUSizeToI16 (DN_USize val);
|
||||
DN_API DN_I32 DN_SaturateCastUSizeToI32 (DN_USize val);
|
||||
DN_API DN_I64 DN_SaturateCastUSizeToI64 (DN_USize val);
|
||||
|
||||
DN_API int DN_SaturateCastU64ToInt (DN_U64 val);
|
||||
DN_API DN_I8 DN_SaturateCastU8ToI8 (DN_U64 val);
|
||||
DN_API DN_I16 DN_SaturateCastU16ToI16 (DN_U64 val);
|
||||
DN_API DN_I32 DN_SaturateCastU32ToI32 (DN_U64 val);
|
||||
DN_API DN_I64 DN_SaturateCastU64ToI64 (DN_U64 val);
|
||||
DN_API DN_UInt DN_SaturateCastU64ToUInt (DN_U64 val);
|
||||
DN_API DN_U8 DN_SaturateCastU64ToU8 (DN_U64 val);
|
||||
DN_API DN_U16 DN_SaturateCastU64ToU16 (DN_U64 val);
|
||||
DN_API DN_U32 DN_SaturateCastU64ToU32 (DN_U64 val);
|
||||
|
||||
DN_API DN_U8 DN_SaturateCastUSizeToU8 (DN_USize val);
|
||||
DN_API DN_U16 DN_SaturateCastUSizeToU16 (DN_USize val);
|
||||
DN_API DN_U32 DN_SaturateCastUSizeToU32 (DN_USize val);
|
||||
DN_API DN_U64 DN_SaturateCastUSizeToU64 (DN_USize val);
|
||||
|
||||
DN_API int DN_SaturateCastISizeToInt (DN_ISize val);
|
||||
DN_API DN_I8 DN_SaturateCastISizeToI8 (DN_ISize val);
|
||||
DN_API DN_I16 DN_SaturateCastISizeToI16 (DN_ISize val);
|
||||
DN_API DN_I32 DN_SaturateCastISizeToI32 (DN_ISize val);
|
||||
DN_API DN_I64 DN_SaturateCastISizeToI64 (DN_ISize val);
|
||||
|
||||
DN_API DN_UInt DN_SaturateCastISizeToUInt (DN_ISize val);
|
||||
DN_API DN_U8 DN_SaturateCastISizeToU8 (DN_ISize val);
|
||||
DN_API DN_U16 DN_SaturateCastISizeToU16 (DN_ISize val);
|
||||
DN_API DN_U32 DN_SaturateCastISizeToU32 (DN_ISize val);
|
||||
DN_API DN_U64 DN_SaturateCastISizeToU64 (DN_ISize val);
|
||||
|
||||
DN_API DN_ISize DN_SaturateCastI64ToISize (DN_I64 val);
|
||||
DN_API DN_I8 DN_SaturateCastI64ToI8 (DN_I64 val);
|
||||
DN_API DN_I16 DN_SaturateCastI64ToI16 (DN_I64 val);
|
||||
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
||||
|
||||
DN_API DN_UInt DN_SaturateCastI64ToUInt (DN_I64 val);
|
||||
DN_API DN_ISize DN_SaturateCastI64ToUSize (DN_I64 val);
|
||||
DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
|
||||
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
||||
DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val);
|
||||
DN_API DN_U64 DN_SaturateCastI64ToU64 (DN_I64 val);
|
||||
|
||||
DN_API DN_I8 DN_SaturateCastIntToI8 (int val);
|
||||
DN_API DN_I16 DN_SaturateCastIntToI16 (int val);
|
||||
DN_API DN_U8 DN_SaturateCastIntToU8 (int val);
|
||||
DN_API DN_U16 DN_SaturateCastIntToU16 (int val);
|
||||
DN_API DN_U32 DN_SaturateCastIntToU32 (int val);
|
||||
DN_API DN_U64 DN_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: DN_Asan ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_ASAN_PoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
||||
DN_API void DN_ASAN_UnpoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
||||
#endif // !defined(DN_BASE_H)
|
90
Base/dn_base_assert.h
Normal file
90
Base/dn_base_assert.h
Normal file
@ -0,0 +1,90 @@
|
||||
#if !defined(DN_BASE_ASSERT_H)
|
||||
#define DN_BASE_ASSERT_H
|
||||
|
||||
#define DN_HardAssert(expr) DN_HardAssertF(expr, "")
|
||||
|
||||
#if defined(DN_FREESTANDING)
|
||||
#define DN_HardAssertF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_DebugBreak; \
|
||||
(*(int *)0) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define DN_HardAssertF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DN_DebugBreak; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(DN_NO_ASSERT)
|
||||
#define DN_Assert(...)
|
||||
#define DN_AssertOnce(...)
|
||||
#define DN_AssertF(...)
|
||||
#define DN_AssertFOnce(...)
|
||||
#else
|
||||
#if defined(DN_FREESTANDING)
|
||||
#define DN_AssertF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_DebugBreak; \
|
||||
(*(int *)0) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define DN_AssertF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DN_DebugBreak; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define DN_AssertFOnce(expr, fmt, ...) \
|
||||
do { \
|
||||
static bool once = true; \
|
||||
if (!(expr) && once) { \
|
||||
once = false; \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DN_DebugBreak; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_Assert(expr) DN_AssertF((expr), "")
|
||||
#define DN_AssertOnce(expr) DN_AssertFOnce((expr), "")
|
||||
#endif
|
||||
|
||||
#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__)
|
||||
#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered")
|
||||
|
||||
// NOTE: Check macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_Check(expr) DN_CheckF(expr, "")
|
||||
|
||||
#if defined(DN_FREESTANDING)
|
||||
#define DN_CheckF(expr, fmt, ...) (expr)
|
||||
#else
|
||||
#if defined(DN_NO_CHECK_BREAK)
|
||||
#define DN_CheckF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
|
||||
#else
|
||||
#define DN_CheckF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTrace_Print(128 /*limit*/), DN_DebugBreak, false))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
160
Base/dn_base_compiler.h
Normal file
160
Base/dn_base_compiler.h
Normal file
@ -0,0 +1,160 @@
|
||||
#if !defined(DN_BASE_COMPILER_H)
|
||||
#define DN_BASE_COMPILER_H
|
||||
|
||||
// NOTE: Compiler identification ///////////////////////////////////////////////////////////////////
|
||||
// Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__clang__)
|
||||
#define DN_COMPILER_CLANG_CL
|
||||
#define DN_COMPILER_CLANG
|
||||
#else
|
||||
#define DN_COMPILER_MSVC
|
||||
#endif
|
||||
#elif defined(__clang__)
|
||||
#define DN_COMPILER_CLANG
|
||||
#elif defined(__GNUC__)
|
||||
#define DN_COMPILER_GCC
|
||||
#endif
|
||||
|
||||
// NOTE: __has_feature /////////////////////////////////////////////////////////////////////////////
|
||||
// MSVC for example does not support the feature detection macro for instance so we compile it out
|
||||
#if defined(__has_feature)
|
||||
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
||||
#else
|
||||
#define DN_HAS_FEATURE(expr) 0
|
||||
#endif
|
||||
|
||||
// NOTE: __has_builtin /////////////////////////////////////////////////////////////////////////////
|
||||
// MSVC for example does not support the feature detection macro for instance so we compile it out
|
||||
#if defined(__has_builtin)
|
||||
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||
#else
|
||||
#define DN_HAS_BUILTIN(expr) 0
|
||||
#endif
|
||||
|
||||
// NOTE: Warning suppression macros ////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_COMPILER_MSVC)
|
||||
#define DN_MSVC_WARNING_PUSH __pragma(warning(push))
|
||||
#define DN_MSVC_WARNING_DISABLE(...) __pragma(warning(disable :##__VA_ARGS__))
|
||||
#define DN_MSVC_WARNING_POP __pragma(warning(pop))
|
||||
#else
|
||||
#define DN_MSVC_WARNING_PUSH
|
||||
#define DN_MSVC_WARNING_DISABLE(...)
|
||||
#define DN_MSVC_WARNING_POP
|
||||
#endif
|
||||
|
||||
#if defined(DN_COMPILER_CLANG) || defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_GCC_WARNING_PUSH _Pragma("GCC diagnostic push")
|
||||
#define DN_GCC_WARNING_DISABLE_HELPER_0(x) #x
|
||||
#define DN_GCC_WARNING_DISABLE_HELPER_1(y) DN_GCC_WARNING_DISABLE_HELPER_0(GCC diagnostic ignored #y)
|
||||
#define DN_GCC_WARNING_DISABLE(warning) _Pragma(DN_GCC_WARNING_DISABLE_HELPER_1(warning))
|
||||
#define DN_GCC_WARNING_POP _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DN_GCC_WARNING_PUSH
|
||||
#define DN_GCC_WARNING_DISABLE(...)
|
||||
#define DN_GCC_WARNING_POP
|
||||
#endif
|
||||
|
||||
// NOTE: Host OS identification ////////////////////////////////////////////////////////////////////
|
||||
#if defined(_WIN32)
|
||||
#define DN_OS_WIN32
|
||||
#elif defined(__gnu_linux__) || defined(__linux__)
|
||||
#define DN_OS_UNIX
|
||||
#endif
|
||||
|
||||
// NOTE: Platform identification ///////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_PLATFORM_EMSCRIPTEN) && \
|
||||
!defined(DN_PLATFORM_POSIX) && \
|
||||
!defined(DN_PLATFORM_WIN32)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define DN_PLATFORM_ARM64
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define DN_PLATFORM_EMSCRIPTEN
|
||||
#elif defined(DN_OS_WIN32)
|
||||
#define DN_PLATFORM_WIN32
|
||||
#else
|
||||
#define DN_PLATFORM_POSIX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Windows crap //////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#if defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED
|
||||
#else
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Force Inline //////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define DN_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
// NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
|
||||
#if defined(DN_STATIC_API)
|
||||
#define DN_API static
|
||||
#else
|
||||
#define DN_API
|
||||
#endif
|
||||
|
||||
// NOTE: C/CPP Literals ////////////////////////////////////////////////////////////////////////////
|
||||
// Declare struct literals that work in both C and C++ because the syntax is different between
|
||||
// languages.
|
||||
#if 0
|
||||
struct Foo { int a; }
|
||||
struct Foo foo = DN_LITERAL(Foo){32}; // Works on both C and C++
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define DN_LITERAL(T) T
|
||||
#else
|
||||
#define DN_LITERAL(T) (T)
|
||||
#endif
|
||||
|
||||
// NOTE: Thread Locals /////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
#define DN_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define DN_THREAD_LOCAL _Thread_local
|
||||
#endif
|
||||
|
||||
// NOTE: C variadic argument annotations ///////////////////////////////////////////////////////////
|
||||
// TODO: Other compilers
|
||||
#if defined(DN_COMPILER_MSVC)
|
||||
#define DN_FMT_ATTRIB _Printf_format_string_
|
||||
#else
|
||||
#define DN_FMT_ATTRIB
|
||||
#endif
|
||||
|
||||
// NOTE: Type Cast /////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_CAST(val) (val)
|
||||
|
||||
// NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
#define DN_ZeroInit {}
|
||||
#else
|
||||
#define DN_ZeroInit {0}
|
||||
#endif
|
||||
|
||||
// NOTE: Address sanitizer /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_ASAN_POISON)
|
||||
#define DN_ASAN_POISON 0
|
||||
#endif
|
||||
|
||||
#if !defined(DN_ASAN_VET_POISON)
|
||||
#define DN_ASAN_VET_POISON 0
|
||||
#endif
|
||||
|
||||
#define DN_ASAN_POISON_ALIGNMENT 8
|
||||
|
||||
#if !defined(DN_ASAN_POISON_GUARD_SIZE)
|
||||
#define DN_ASAN_POISON_GUARD_SIZE 128
|
||||
#endif
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
#endif // !defined(DN_BASE_COMPILER_H)
|
1290
Base/dn_base_containers.cpp
Normal file
1290
Base/dn_base_containers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
277
Base/dn_base_containers.h
Normal file
277
Base/dn_base_containers.h
Normal file
@ -0,0 +1,277 @@
|
||||
#if !defined(DN_CONTAINERS_H)
|
||||
#define DN_CONTAINERS_H
|
||||
|
||||
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_ArrayErase
|
||||
{
|
||||
DN_ArrayErase_Unstable,
|
||||
DN_ArrayErase_Stable,
|
||||
};
|
||||
|
||||
struct DN_ArrayEraseResult
|
||||
{
|
||||
// The next index your for-index should be set to such that you can continue
|
||||
// to iterate the remainder of the array, e.g:
|
||||
//
|
||||
// for (DN_USize index = 0; index < array.size; index++) {
|
||||
// if (erase)
|
||||
// index = DN_FArray_EraseRange(&array, index, -3, DN_ArrayErase_Unstable);
|
||||
// }
|
||||
DN_USize it_index;
|
||||
DN_USize items_erased; // The number of items erased
|
||||
};
|
||||
|
||||
template <typename T> struct DN_ArrayFindResult
|
||||
{
|
||||
T *data; // Pointer to the value if a match is found, null pointer otherwise
|
||||
DN_USize index; // Index to the value if a match is found, 0 otherwise
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_SARRAY)
|
||||
// NOTE: DN_SArray /////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> struct DN_SArray
|
||||
{
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
DN_USize max; // Maximum number of items this array can store
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
#endif // !defined(DN_NO_SARRAY)
|
||||
|
||||
#if !defined(DN_NO_FARRAY)
|
||||
// NOTE: DN_FArray /////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, DN_USize N> struct DN_FArray
|
||||
{
|
||||
T data[N]; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
template <typename T> using DN_FArray8 = DN_FArray<T, 8>;
|
||||
template <typename T> using DN_FArray16 = DN_FArray<T, 16>;
|
||||
template <typename T> using DN_FArray32 = DN_FArray<T, 32>;
|
||||
template <typename T> using DN_FArray64 = DN_FArray<T, 64>;
|
||||
#endif // !defined(DN_NO_FARRAY)
|
||||
|
||||
#if !defined(DN_NO_DSMAP)
|
||||
// NOTE: DN_DSMap //////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_DSMapKeyType
|
||||
{
|
||||
// Key | Key Hash | Map Index
|
||||
DN_DSMapKeyType_Invalid,
|
||||
DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size
|
||||
DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size
|
||||
DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size
|
||||
DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size
|
||||
};
|
||||
|
||||
struct DN_DSMapKey
|
||||
{
|
||||
DN_DSMapKeyType type;
|
||||
DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches
|
||||
void const *buffer_data;
|
||||
DN_U32 buffer_size;
|
||||
DN_U64 u64;
|
||||
bool no_copy_buffer;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DN_DSMapSlot
|
||||
{
|
||||
DN_DSMapKey key; // Hash table lookup key
|
||||
T value; // Hash table value
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_DSMapFlags;
|
||||
enum DN_DSMapFlags_
|
||||
{
|
||||
DN_DSMapFlags_Nil = 0,
|
||||
DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0,
|
||||
};
|
||||
|
||||
using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed);
|
||||
template <typename T> struct DN_DSMap
|
||||
{
|
||||
DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array
|
||||
DN_DSMapSlot<T> *slots; // Values of the array stored contiguously, non-sorted order
|
||||
DN_U32 size; // Total capacity of the map and is a power of two
|
||||
DN_U32 occupied; // Number of slots used in the hash table
|
||||
DN_Arena *arena; // Backing arena for the hash table
|
||||
DN_Pool pool; // Allocator for keys that are variable-sized buffers
|
||||
DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size
|
||||
DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set
|
||||
DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used
|
||||
DN_DSMapFlags flags;
|
||||
};
|
||||
|
||||
template <typename T> struct DN_DSMapResult
|
||||
{
|
||||
bool found;
|
||||
DN_DSMapSlot<T> *slot;
|
||||
T *value;
|
||||
};
|
||||
#endif // !defined(DN_NO_DSMAP)
|
||||
|
||||
#if !defined(DN_NO_LIST)
|
||||
// NOTE: DN_List ///////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> struct DN_ListChunk
|
||||
{
|
||||
T *data;
|
||||
DN_USize size;
|
||||
DN_USize count;
|
||||
DN_ListChunk<T> *next;
|
||||
DN_ListChunk<T> *prev;
|
||||
};
|
||||
|
||||
template <typename T> struct DN_ListIterator
|
||||
{
|
||||
DN_B32 init; // True if DN_List_Iterate has been called at-least once on this iterator
|
||||
DN_ListChunk<T> *chunk; // The chunk that the iterator is reading from
|
||||
DN_USize chunk_data_index; // The index in the chunk the iterator is referencing
|
||||
DN_USize index; // The index of the item in the list as if it was flat array
|
||||
T *data; // Pointer to the data the iterator is referencing. Nullptr if invalid.
|
||||
};
|
||||
|
||||
template <typename T> struct DN_List
|
||||
{
|
||||
DN_USize count; // Cumulative count of all items made across all list chunks
|
||||
DN_USize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node.
|
||||
DN_ListChunk<T> *head;
|
||||
DN_ListChunk<T> *tail;
|
||||
};
|
||||
#endif // !defined(DN_NO_LIST)
|
||||
|
||||
template <typename T> DN_ArrayEraseResult DN_CArray_EraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> T * DN_CArray_MakeArray (T *data, DN_USize *size, DN_USize max, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_CArray_InsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T> T DN_CArray_PopFront (T *data, DN_USize *size, DN_USize count);
|
||||
template <typename T> T DN_CArray_PopBack (T *data, DN_USize *size, DN_USize count);
|
||||
template <typename T> DN_ArrayFindResult<T> DN_CArray_Find (T *data, DN_USize size, T const &value);
|
||||
|
||||
// NOTE: DN_SArray /////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_SARRAY)
|
||||
template <typename T> DN_SArray<T> DN_SArray_Init (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
|
||||
template <typename T> DN_SArray<T> DN_SArray_InitSlice (DN_Arena *arena, DN_Slice<T> slice, DN_USize size, DN_ZeroMem zero_mem);
|
||||
template <typename T, size_t N> DN_SArray<T> DN_SArray_InitCArray (DN_Arena *arena, T const (&array)[N], DN_USize size, DN_ZeroMem);
|
||||
template <typename T> DN_SArray<T> DN_SArray_InitBuffer (T* buffer, DN_USize size);
|
||||
template <typename T> bool DN_SArray_IsValid (DN_SArray<T> const *array);
|
||||
template <typename T> DN_Slice<T> DN_SArray_Slice (DN_SArray<T> const *array);
|
||||
template <typename T> T * DN_SArray_AddArray (DN_SArray<T> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_SArray_AddCArray (DN_SArray<T> *array, T const (&items)[N]);
|
||||
template <typename T> T * DN_SArray_Add (DN_SArray<T> *array, T const &item);
|
||||
#define DN_SArray_AddArrayAssert(...) DN_HardAssert(DN_SArray_AddArray(__VA_ARGS__))
|
||||
#define DN_SArray_AddCArrayAssert(...) DN_HardAssert(DN_SArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_SArray_AddAssert(...) DN_HardAssert(DN_SArray_Add(__VA_ARGS__))
|
||||
template <typename T> T * DN_SArray_MakeArray (DN_SArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_SArray_Make (DN_SArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_SArray_MakeArrayAssert(...) DN_HardAssert(DN_SArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_SArray_MakeAssert(...) DN_HardAssert(DN_SArray_Make(__VA_ARGS__))
|
||||
template <typename T> T * DN_SArray_InsertArray (DN_SArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_SArray_InsertCArray (DN_SArray<T> *array, DN_USize index, T const (&items)[N]);
|
||||
template <typename T> T * DN_SArray_Insert (DN_SArray<T> *array, DN_USize index, T const &item);
|
||||
#define DN_SArray_InsertArrayAssert(...) DN_HardAssert(DN_SArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_SArray_InsertCArrayAssert(...) DN_HardAssert(DN_SArray_InsertCArray(__VA_ARGS__))
|
||||
#define DN_SArray_InsertAssert(...) DN_HardAssert(DN_SArray_Insert(__VA_ARGS__))
|
||||
template <typename T> T DN_SArray_PopFront (DN_SArray<T> *array, DN_USize count);
|
||||
template <typename T> T DN_SArray_PopBack (DN_SArray<T> *array, DN_USize count);
|
||||
template <typename T> DN_ArrayEraseResult DN_SArray_EraseRange (DN_SArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> void DN_SArray_Clear (DN_SArray<T> *array);
|
||||
#endif // !defined(DN_NO_SARRAY)
|
||||
|
||||
#if !defined(DN_NO_FARRAY)
|
||||
#define DN_FArray_ToSArray(array) DN_SArray_InitBuffer((array)->data, DN_ArrayCountU((array)->data))
|
||||
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_Init (T const *array, DN_USize count);
|
||||
#define DN_FArray_HasData(array) ((array).data && (array).size)
|
||||
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_InitSlice (DN_Slice<T> slice);
|
||||
template <typename T, DN_USize N, DN_USize K> DN_FArray<T, N> DN_FArray_InitCArray (T const (&items)[K]);
|
||||
template <typename T, DN_USize N> bool DN_FArray_IsValid (DN_FArray<T, N> const *array);
|
||||
template <typename T, DN_USize N> DN_USize DN_FArray_Max (DN_FArray<T, N> const *) { return N; }
|
||||
template <typename T, DN_USize N> DN_Slice<T> DN_FArray_Slice (DN_FArray<T, N> const *array);
|
||||
template <typename T, DN_USize N> T * DN_FArray_AddArray (DN_FArray<T, N> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_AddCArray (DN_FArray<T, N> *array, T const (&items)[K]);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Add (DN_FArray<T, N> *array, T const &item);
|
||||
#define DN_FArray_AddArrayAssert(...) DN_HardAssert(DN_FArray_AddArray(__VA_ARGS__))
|
||||
#define DN_FArray_AddCArrayAssert(...) DN_HardAssert(DN_FArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_FArray_AddAssert(...) DN_HardAssert(DN_FArray_Add(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T * DN_FArray_MakeArray (DN_FArray<T, N> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Make (DN_FArray<T, N> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_FArray_MakeArrayAssert(...) DN_HardAssert(DN_FArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_FArray_MakeAssert(...) DN_HardAssert(DN_FArray_Make(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T * DN_FArray_InsertArray (DN_FArray<T, N> *array, T const &item, DN_USize index);
|
||||
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_InsertCArray (DN_FArray<T, N> *array, DN_USize index, T const (&items)[K]);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Insert (DN_FArray<T, N> *array, DN_USize index, T const &item);
|
||||
#define DN_FArray_InsertArrayAssert(...) DN_HardAssert(DN_FArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_FArray_InsertAssert(...) DN_HardAssert(DN_FArray_Insert(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T DN_FArray_PopFront (DN_FArray<T, N> *array, DN_USize count);
|
||||
template <typename T, DN_USize N> T DN_FArray_PopBack (DN_FArray<T, N> *array, DN_USize count);
|
||||
template <typename T, DN_USize N> DN_ArrayFindResult<T> DN_FArray_Find (DN_FArray<T, N> *array, T const &find);
|
||||
template <typename T, DN_USize N> DN_ArrayEraseResult DN_FArray_EraseRange (DN_FArray<T, N> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T, DN_USize N> void DN_FArray_Clear (DN_FArray<T, N> *array);
|
||||
#endif // !defined(DN_NO_FARRAY)
|
||||
|
||||
#if !defined(DN_NO_SLICE)
|
||||
#define DN_TO_SLICE(val) DN_Slice_Init((val)->data, (val)->size)
|
||||
#define DN_Slice_InitCArray(array) DN_Slice_Init(array, DN_ArrayCountU(array))
|
||||
template <typename T> DN_Slice<T> DN_Slice_Init (T* const data, DN_USize size);
|
||||
template <typename T, DN_USize N> DN_Slice<T> DN_Slice_InitCArrayCopy (DN_Arena *arena, T const (&array)[N]);
|
||||
template <typename T> DN_Slice<T> DN_Slice_Copy (DN_Arena *arena, DN_Slice<T> slice);
|
||||
template <typename T> DN_Slice<T> DN_Slice_CopyPtr (DN_Arena *arena, T* const data, DN_USize size);
|
||||
template <typename T> DN_Slice<T> DN_Slice_Alloc (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_Str8 DN_Slice_Str8Render (DN_Arena *arena, DN_Slice<DN_Str8> array, DN_Str8 separator);
|
||||
DN_Str8 DN_Slice_Str8RenderSpaceSeparated (DN_Arena *arena, DN_Slice<DN_Str8> array);
|
||||
DN_Str16 DN_Slice_Str16Render (DN_Arena *arena, DN_Slice<DN_Str16> array, DN_Str16 separator);
|
||||
DN_Str16 DN_Slice_Str16RenderSpaceSeparated(DN_Arena *arena, DN_Slice<DN_Str16> array);
|
||||
#endif // !defined(DN_NO_SLICE)
|
||||
|
||||
#if !defined(DN_NO_DSMAP)
|
||||
template <typename T> DN_DSMap<T> DN_DSMap_Init (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
||||
template <typename T> void DN_DSMap_Deinit (DN_DSMap<T> *map, DN_ZeroMem zero_mem);
|
||||
template <typename T> bool DN_DSMap_IsValid (DN_DSMap<T> const *map);
|
||||
template <typename T> DN_U32 DN_DSMap_Hash (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_U32 DN_DSMap_HashToSlotIndex (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Find (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Make (DN_DSMap<T> *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Set (DN_DSMap<T> *map, DN_DSMapKey key, T const &value);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyU64 (DN_DSMap<T> const *map, DN_U64 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyU64 (DN_DSMap<T> *map, DN_U64 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyU64 (DN_DSMap<T> *map, DN_U64 key, T const &value);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyStr8 (DN_DSMap<T> const *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyStr8 (DN_DSMap<T> *map, DN_Str8 key, T const &value);
|
||||
template <typename T> bool DN_DSMap_Resize (DN_DSMap<T> *map, DN_U32 size);
|
||||
template <typename T> bool DN_DSMap_Erase (DN_DSMap<T> *map, DN_DSMapKey key);
|
||||
template <typename T> bool DN_DSMap_EraseKeyU64 (DN_DSMap<T> *map, DN_U64 key);
|
||||
template <typename T> bool DN_DSMap_EraseKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyBuffer (DN_DSMap<T> const *map, void const *data, DN_U32 size);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyBufferAsU64NoHash (DN_DSMap<T> const *map, void const *data, DN_U32 size);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyU64 (DN_DSMap<T> const *map, DN_U64 u64);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyStr8 (DN_DSMap<T> const *map, DN_Str8 string);
|
||||
#define DN_DSMap_KeyCStr8(map, string) DN_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1)
|
||||
DN_API DN_DSMapKey DN_DSMap_KeyU64NoHash (DN_U64 u64);
|
||||
DN_API bool DN_DSMap_KeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||
DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||
#endif // !defined(DN_NO_DSMAP)
|
||||
|
||||
#if !defined(DN_NO_LIST)
|
||||
template <typename T> DN_List<T> DN_List_Init (DN_USize chunk_size);
|
||||
template <typename T, size_t N> DN_List<T> DN_List_InitCArray (DN_Arena *arena, DN_USize chunk_size, T const (&array)[N]);
|
||||
template <typename T> T * DN_List_At (DN_List<T> *list, DN_USize index, DN_ListChunk<T> **at_chunk);
|
||||
template <typename T> void DN_List_Clear (DN_List<T> *list);
|
||||
template <typename T> bool DN_List_Iterate (DN_List<T> *list, DN_ListIterator<T> *it, DN_USize start_index);
|
||||
template <typename T> T * DN_List_MakeArena (DN_List<T> *list, DN_Arena *arena, DN_USize count);
|
||||
template <typename T> T * DN_List_MakePool (DN_List<T> *list, DN_Pool *pool, DN_USize count);
|
||||
template <typename T> T * DN_List_AddArena (DN_List<T> *list, DN_Arena *arena, T const &value);
|
||||
template <typename T> T * DN_List_AddPool (DN_List<T> *list, DN_Pool *pool, T const &value);
|
||||
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Arena *arena, DN_List<T> other);
|
||||
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Pool *pool, DN_List<T> other);
|
||||
template <typename T> DN_Slice<T> DN_List_ToSliceCopy (DN_List<T> const *list, DN_Arena* arena);
|
||||
#endif // !defined(DN_NO_LIST)
|
||||
#endif // !defined(DN_CONTAINER_H)
|
364
Base/dn_base_convert.cpp
Normal file
364
Base/dn_base_convert.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
#define DN_CONVERT_CPP
|
||||
|
||||
DN_API int DN_CVT_FmtBuffer3DotTruncate(char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int size_required = DN_VSNPrintF(buffer, size, fmt, args);
|
||||
int result = DN_Max(DN_Min(size_required, size - 1), 0);
|
||||
if (result == size - 1) {
|
||||
buffer[size - 2] = '.';
|
||||
buffer[size - 3] = '.';
|
||||
}
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_CVTU64Str8 DN_CVT_U64ToStr8(uint64_t val, char separator)
|
||||
{
|
||||
DN_CVTU64Str8 result = {};
|
||||
if (val == 0) {
|
||||
result.data[result.size++] = '0';
|
||||
} else {
|
||||
// NOTE: The number is written in reverse because we form the string by
|
||||
// dividing by 10, so we write it in, then reverse it out after all is
|
||||
// done.
|
||||
DN_CVTU64Str8 temp = {};
|
||||
for (DN_USize digit_count = 0; val > 0; digit_count++) {
|
||||
if (separator && (digit_count != 0) && (digit_count % 3 == 0))
|
||||
temp.data[temp.size++] = separator;
|
||||
|
||||
auto digit = DN_CAST(char)(val % 10);
|
||||
temp.data[temp.size++] = '0' + digit;
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
// NOTE: Reverse the string
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop
|
||||
DN_MSVC_WARNING_DISABLE(6385) // Reading invalid data from 'temp.data' unsigned overflow is valid for loop termination
|
||||
for (DN_USize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
|
||||
char ch = temp.data[temp_index];
|
||||
result.data[result.size++] = ch;
|
||||
}
|
||||
DN_MSVC_WARNING_POP
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_CVTU64ByteSize DN_CVT_U64ToByteSize(uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
|
||||
{
|
||||
DN_CVTU64ByteSize result = {};
|
||||
result.bytes = DN_CAST(DN_F64) bytes;
|
||||
if (!DN_Check(desired_type != DN_CVTU64ByteSizeType_Count)) {
|
||||
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (desired_type == DN_CVTU64ByteSizeType_Auto)
|
||||
for (; result.type < DN_CVTU64ByteSizeType_Count && result.bytes >= 1024.0; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
|
||||
result.bytes /= 1024.0;
|
||||
else
|
||||
for (; result.type < desired_type; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
|
||||
result.bytes /= 1024.0;
|
||||
|
||||
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_U64ToByteSizeStr8(DN_Arena *arena, uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
|
||||
{
|
||||
DN_CVTU64ByteSize byte_size = DN_CVT_U64ToByteSize(bytes, desired_type);
|
||||
DN_Str8 result = DN_Str8_InitF(arena, "%.2f%.*s", byte_size.bytes, DN_STR_FMT(byte_size.suffix));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_U64ByteSizeTypeString(DN_CVTU64ByteSizeType type)
|
||||
{
|
||||
DN_Str8 result = DN_STR8("");
|
||||
switch (type) {
|
||||
case DN_CVTU64ByteSizeType_B: result = DN_STR8("B"); break;
|
||||
case DN_CVTU64ByteSizeType_KiB: result = DN_STR8("KiB"); break;
|
||||
case DN_CVTU64ByteSizeType_MiB: result = DN_STR8("MiB"); break;
|
||||
case DN_CVTU64ByteSizeType_GiB: result = DN_STR8("GiB"); break;
|
||||
case DN_CVTU64ByteSizeType_TiB: result = DN_STR8("TiB"); break;
|
||||
case DN_CVTU64ByteSizeType_Count: result = DN_STR8(""); break;
|
||||
case DN_CVTU64ByteSizeType_Auto: result = DN_STR8(""); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_U64ToAge(DN_Arena *arena, DN_U64 age_s, DN_CVTU64AgeUnit unit)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
char buffer[512];
|
||||
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
|
||||
DN_U64 remainder = age_s;
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Year) {
|
||||
DN_USize value = remainder / DN_YearsToSec(1);
|
||||
remainder -= DN_YearsToSec(value);
|
||||
if (value)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zuyr", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Week) {
|
||||
DN_USize value = remainder / DN_WeeksToSec(1);
|
||||
remainder -= DN_WeeksToSec(value);
|
||||
if (value)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zuw", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Day) {
|
||||
DN_USize value = remainder / DN_DaysToSec(1);
|
||||
remainder -= DN_DaysToSec(value);
|
||||
if (value)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zud", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Hr) {
|
||||
DN_USize value = remainder / DN_HoursToSec(1);
|
||||
remainder -= DN_HoursToSec(value);
|
||||
if (value)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zuh", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Min) {
|
||||
DN_USize value = remainder / DN_MinutesToSec(1);
|
||||
remainder -= DN_MinutesToSec(value);
|
||||
if (value)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zum", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Sec) {
|
||||
DN_USize value = remainder;
|
||||
if (value || builder.string_size == 0)
|
||||
DN_Str8Builder_AppendF(&builder, "%s%zus", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_F64ToAge(DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit unit)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
char buffer[128];
|
||||
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
|
||||
DN_F64 remainder = age_s;
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Year) {
|
||||
DN_F64 value = remainder / DN_CAST(DN_F64) DN_YearsToSec(1);
|
||||
if (value >= 1.0) {
|
||||
remainder -= DN_YearsToSec(value);
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fyr", builder.string_size ? " " : "", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Week) {
|
||||
DN_F64 value = remainder / DN_CAST(DN_F64) DN_WeeksToSec(1);
|
||||
if (value >= 1.0) {
|
||||
remainder -= DN_WeeksToSec(value);
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fw", builder.string_size ? " " : "", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Day) {
|
||||
DN_F64 value = remainder / DN_CAST(DN_F64) DN_DaysToSec(1);
|
||||
if (value >= 1.0) {
|
||||
remainder -= DN_WeeksToSec(value);
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fd", builder.string_size ? " " : "", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Hr) {
|
||||
DN_F64 value = remainder / DN_CAST(DN_F64) DN_HoursToSec(1);
|
||||
if (value >= 1.0) {
|
||||
remainder -= DN_HoursToSec(value);
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fh", builder.string_size ? " " : "", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Min) {
|
||||
DN_F64 value = remainder / DN_CAST(DN_F64) DN_MinutesToSec(1);
|
||||
if (value >= 1.0) {
|
||||
remainder -= DN_MinutesToSec(value);
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fm", builder.string_size ? " " : "", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (unit & DN_CVTU64AgeUnit_Sec) {
|
||||
DN_F64 value = remainder;
|
||||
DN_Str8Builder_AppendF(&builder, "%s%.1fs", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_CVT_HexToU64(DN_Str8 hex)
|
||||
{
|
||||
DN_Str8 real_hex = DN_Str8_TrimPrefix(DN_Str8_TrimPrefix(hex, DN_STR8("0x")), DN_STR8("0X"));
|
||||
DN_USize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
|
||||
DN_Assert(real_hex.size <= max_hex_size);
|
||||
|
||||
DN_USize size = DN_Min(max_hex_size, real_hex.size);
|
||||
uint64_t result = 0;
|
||||
for (DN_USize index = 0; index < size; index++) {
|
||||
char ch = real_hex.data[index];
|
||||
DN_CharHexToU8 val = DN_Char_HexToU8(ch);
|
||||
if (!val.success)
|
||||
break;
|
||||
result = (result << 4) | val.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_U64ToHex(DN_Arena *arena, uint64_t number, uint32_t flags)
|
||||
{
|
||||
DN_Str8 prefix = {};
|
||||
if ((flags & DN_CVTHexU64Str8Flags_0xPrefix))
|
||||
prefix = DN_STR8("0x");
|
||||
|
||||
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
DN_USize required_size = DN_CStr8_FSize(fmt, number) + prefix.size;
|
||||
DN_Str8 result = DN_Str8_Alloc(arena, required_size, DN_ZeroMem_No);
|
||||
|
||||
if (DN_Str8_HasData(result)) {
|
||||
DN_Memcpy(result.data, prefix.data, prefix.size);
|
||||
int space = DN_CAST(int) DN_Max((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
||||
DN_SNPrintF(result.data + prefix.size, space, fmt, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_CVTU64HexStr8 DN_CVT_U64ToHexStr8(uint64_t number, DN_CVTHexU64Str8Flags flags)
|
||||
{
|
||||
DN_Str8 prefix = {};
|
||||
if (flags & DN_CVTHexU64Str8Flags_0xPrefix)
|
||||
prefix = DN_STR8("0x");
|
||||
|
||||
DN_CVTU64HexStr8 result = {};
|
||||
DN_Memcpy(result.data, prefix.data, prefix.size);
|
||||
result.size += DN_CAST(int8_t) prefix.size;
|
||||
|
||||
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = DN_SNPrintF(result.data + result.size, DN_ArrayCountU(result.data) - result.size, fmt, number);
|
||||
result.size += DN_CAST(uint8_t) size;
|
||||
DN_Assert(result.size < DN_ArrayCountU(result.data));
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not, but, always null terminates so
|
||||
// the last byte is wasted.
|
||||
result.size = DN_Min(result.size, DN_ArrayCountU(result.data) - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_CVT_BytesToHexPtr(void const *src, DN_USize src_size, char *dest, DN_USize dest_size)
|
||||
{
|
||||
if (!src || !dest)
|
||||
return false;
|
||||
|
||||
if (!DN_Check(dest_size >= src_size * 2))
|
||||
return false;
|
||||
|
||||
char const *HEX = "0123456789abcdef";
|
||||
unsigned char const *src_u8 = DN_CAST(unsigned char const *) src;
|
||||
for (DN_USize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index++] = HEX[(int)hex01];
|
||||
dest[dest_index++] = HEX[(int)hex02];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_BytesToHex(DN_Arena *arena, void const *src, DN_USize size)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!src || size <= 0)
|
||||
return result;
|
||||
|
||||
result = DN_Str8_Alloc(arena, size * 2, DN_ZeroMem_No);
|
||||
result.data[result.size] = 0;
|
||||
bool converted = DN_CVT_BytesToHexPtr(src, size, result.data, result.size);
|
||||
DN_Assert(converted);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_CVT_HexToBytesPtrUnchecked(DN_Str8 hex, void *dest, DN_USize dest_size)
|
||||
{
|
||||
DN_USize result = 0;
|
||||
unsigned char *dest_u8 = DN_CAST(unsigned char *) dest;
|
||||
|
||||
for (DN_USize hex_index = 0; hex_index < hex.size; hex_index += 2, result += 1) {
|
||||
char hex01 = hex.data[hex_index];
|
||||
char hex02 = (hex_index + 1 < hex.size) ? hex.data[hex_index + 1] : 0;
|
||||
char bit4_01 = DN_Char_HexToU8(hex01).value;
|
||||
char bit4_02 = DN_Char_HexToU8(hex02).value;
|
||||
char byte = (bit4_01 << 4) | (bit4_02 << 0);
|
||||
dest_u8[result] = byte;
|
||||
}
|
||||
|
||||
DN_Assert(result <= dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_CVT_HexToBytesPtr(DN_Str8 hex, void *dest, DN_USize dest_size)
|
||||
{
|
||||
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
|
||||
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
|
||||
|
||||
DN_USize result = 0;
|
||||
if (!DN_Str8_HasData(hex))
|
||||
return result;
|
||||
|
||||
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
||||
// Either way, the size can be odd or even, hence we round up to the nearest
|
||||
// multiple of two to ensure that we calculate the min buffer size orrectly.
|
||||
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
|
||||
DN_USize min_buffer_size = hex_size_rounded_up / 2;
|
||||
if (hex.size <= 0 || !DN_Check(dest_size >= min_buffer_size))
|
||||
return result;
|
||||
|
||||
result = DN_CVT_HexToBytesPtrUnchecked(hex, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_HexToBytesUnchecked(DN_Arena *arena, DN_Str8 hex)
|
||||
{
|
||||
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
|
||||
DN_Str8 result = DN_Str8_Alloc(arena, (hex_size_rounded_up / 2), DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
DN_USize bytes_written = DN_CVT_HexToBytesPtr(hex, result.data, result.size);
|
||||
DN_Assert(bytes_written == result.size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_CVT_HexToBytes(DN_Arena *arena, DN_Str8 hex)
|
||||
{
|
||||
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
|
||||
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
|
||||
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Str8_HasData(hex))
|
||||
return result;
|
||||
|
||||
if (!DN_Check(DN_Str8_IsAll(hex, DN_Str8IsAll_Hex)))
|
||||
return result;
|
||||
|
||||
result = DN_CVT_HexToBytesUnchecked(arena, hex);
|
||||
return result;
|
||||
}
|
77
Base/dn_base_convert.h
Normal file
77
Base/dn_base_convert.h
Normal file
@ -0,0 +1,77 @@
|
||||
#if !defined(DN_BASE_CONVERT_H)
|
||||
#define DN_BASE_CONVERT_H
|
||||
|
||||
struct DN_CVTU64Str8
|
||||
{
|
||||
char data[27 + 1]; // NOTE(dn): 27 is the maximum size of DN_U64 including a separator
|
||||
DN_U8 size;
|
||||
};
|
||||
|
||||
enum DN_CVTU64ByteSizeType
|
||||
{
|
||||
DN_CVTU64ByteSizeType_B,
|
||||
DN_CVTU64ByteSizeType_KiB,
|
||||
DN_CVTU64ByteSizeType_MiB,
|
||||
DN_CVTU64ByteSizeType_GiB,
|
||||
DN_CVTU64ByteSizeType_TiB,
|
||||
DN_CVTU64ByteSizeType_Count,
|
||||
DN_CVTU64ByteSizeType_Auto,
|
||||
};
|
||||
|
||||
struct DN_CVTU64ByteSize
|
||||
{
|
||||
DN_CVTU64ByteSizeType type;
|
||||
DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
|
||||
DN_F64 bytes;
|
||||
};
|
||||
|
||||
struct DN_CVTU64HexStr8
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
DN_U8 size;
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_CVTHexU64Str8Flags;
|
||||
enum DN_CVTHexU64Str8Flags_
|
||||
{
|
||||
DN_CVTHexU64Str8Flags_Nil = 0,
|
||||
DN_CVTHexU64Str8Flags_0xPrefix = 1 << 0, /// Add the '0x' prefix from the string
|
||||
DN_CVTHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_CVTU64AgeUnit;
|
||||
enum DN_CVTU64AgeUnit_
|
||||
{
|
||||
DN_CVTU64AgeUnit_Sec = 1 << 0,
|
||||
DN_CVTU64AgeUnit_Min = 1 << 1,
|
||||
DN_CVTU64AgeUnit_Hr = 1 << 2,
|
||||
DN_CVTU64AgeUnit_Day = 1 << 3,
|
||||
DN_CVTU64AgeUnit_Week = 1 << 4,
|
||||
DN_CVTU64AgeUnit_Year = 1 << 5,
|
||||
DN_CVTU64AgeUnit_HMS = DN_CVTU64AgeUnit_Sec | DN_CVTU64AgeUnit_Min | DN_CVTU64AgeUnit_Hr,
|
||||
DN_CVTU64AgeUnit_All = DN_CVTU64AgeUnit_HMS | DN_CVTU64AgeUnit_Day | DN_CVTU64AgeUnit_Week | DN_CVTU64AgeUnit_Year,
|
||||
};
|
||||
|
||||
DN_API int DN_CVT_FmtBuffer3DotTruncate (char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_CVTU64Str8 DN_CVT_U64ToStr8 (DN_U64 val, char separator);
|
||||
DN_API DN_CVTU64ByteSize DN_CVT_U64ToByteSize (DN_U64 bytes, DN_CVTU64ByteSizeType type);
|
||||
DN_API DN_Str8 DN_CVT_U64ToByteSizeStr8 (DN_Arena *arena, DN_U64 bytes, DN_CVTU64ByteSizeType desired_type);
|
||||
DN_API DN_Str8 DN_CVT_U64ByteSizeTypeString (DN_CVTU64ByteSizeType type);
|
||||
DN_API DN_Str8 DN_CVT_U64ToAge (DN_Arena *arena, DN_U64 age_s, DN_CVTU64AgeUnit unit);
|
||||
DN_API DN_Str8 DN_CVT_F64ToAge (DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit unit);
|
||||
|
||||
DN_API DN_U64 DN_CVT_HexToU64 (DN_Str8 hex);
|
||||
DN_API DN_Str8 DN_CVT_U64ToHex (DN_Arena *arena, DN_U64 number, DN_CVTHexU64Str8Flags flags);
|
||||
DN_API DN_CVTU64HexStr8 DN_CVT_U64ToHexStr8 (DN_U64 number, DN_U32 flags);
|
||||
|
||||
DN_API bool DN_CVT_BytesToHexPtr (void const *src, DN_USize src_size, char *dest, DN_USize dest_size);
|
||||
DN_API DN_Str8 DN_CVT_BytesToHex (DN_Arena *arena, void const *src, DN_USize size);
|
||||
#define DN_CVT_BytesToHexFromTLS(...) DN_CVT_BytesToHex(DN_OS_TLSTopArena(), __VA_ARGS__)
|
||||
|
||||
DN_API DN_USize DN_CVT_HexToBytesPtrUnchecked (DN_Str8 hex, void *dest, DN_USize dest_size);
|
||||
DN_API DN_USize DN_CVT_HexToBytesPtr (DN_Str8 hex, void *dest, DN_USize dest_size);
|
||||
DN_API DN_Str8 DN_CVT_HexToBytesUnchecked (DN_Arena *arena, DN_Str8 hex);
|
||||
#define DN_CVT_HexToBytesUncheckedFromTLS(...) DN_CVT_HexToBytesUnchecked(DN_OS_TLSTopArena(), __VA_ARGS__)
|
||||
DN_API DN_Str8 DN_CVT_HexToBytes (DN_Arena *arena, DN_Str8 hex);
|
||||
#define DN_CVT_HexToBytesFromTLS(...) DN_CVT_HexToBytes(DN_OS_TLSTopArena(), __VA_ARGS__)
|
||||
#endif // defined(DN_BASE_CONVERT_H)
|
125
Base/dn_base_log.cpp
Normal file
125
Base/dn_base_log.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#define DN_BASE_LOG_CPP
|
||||
|
||||
#include "../dn_clangd.h"
|
||||
|
||||
static DN_LOGEmitFromTypeFVFunc *g_dn_base_log_emit_from_type_fv_func_;
|
||||
static void *g_dn_base_log_emit_from_type_fv_user_context_;
|
||||
|
||||
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType colour, DN_U8 r, DN_U8 g, DN_U8 b)
|
||||
{
|
||||
DN_THREAD_LOCAL char buffer[32];
|
||||
buffer[0] = 0;
|
||||
DN_Str8 result = {};
|
||||
result.size = DN_SNPrintF(buffer,
|
||||
DN_ArrayCountU(buffer),
|
||||
"\x1b[%d;2;%u;%u;%um",
|
||||
colour == DN_LOGColourType_Fg ? 38 : 48,
|
||||
r,
|
||||
g,
|
||||
b);
|
||||
result.data = buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromU32(DN_LOGColourType colour, DN_U32 value)
|
||||
{
|
||||
DN_U8 r = DN_CAST(DN_U8)(value >> 24);
|
||||
DN_U8 g = DN_CAST(DN_U8)(value >> 16);
|
||||
DN_U8 b = DN_CAST(DN_U8)(value >> 8);
|
||||
DN_Str8 result = DN_LOG_ColourEscapeCodeStr8FromRGB(colour, r, g, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_LOGPrefixSize DN_LOG_MakePrefix(DN_LOGStyle style, DN_LOGTypeParam type, DN_CallSite call_site, DN_LOGDate date, char *dest, DN_USize dest_size)
|
||||
{
|
||||
DN_Str8 type_str8 = type.str8;
|
||||
if (type.is_u32_enum) {
|
||||
switch (type.u32) {
|
||||
case DN_LOGType_Debug: type_str8 = DN_STR8("DEBUG"); break;
|
||||
case DN_LOGType_Info: type_str8 = DN_STR8("INFO "); break;
|
||||
case DN_LOGType_Warning: type_str8 = DN_STR8("WARN"); break;
|
||||
case DN_LOGType_Error: type_str8 = DN_STR8("ERROR"); break;
|
||||
case DN_LOGType_Count: type_str8 = DN_STR8("BADXX"); break;
|
||||
}
|
||||
}
|
||||
|
||||
static DN_USize max_type_length = 0;
|
||||
max_type_length = DN_Max(max_type_length, type_str8.size);
|
||||
int type_padding = DN_CAST(int)(max_type_length - type_str8.size);
|
||||
|
||||
DN_Str8 colour_esc = {};
|
||||
DN_Str8 bold_esc = {};
|
||||
DN_Str8 reset_esc = {};
|
||||
if (style.colour) {
|
||||
bold_esc = DN_STR8(DN_LOG_BoldEscapeCode);
|
||||
reset_esc = DN_STR8(DN_LOG_ResetEscapeCode);
|
||||
colour_esc = DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b);
|
||||
}
|
||||
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(call_site.file);
|
||||
DN_GCC_WARNING_PUSH
|
||||
DN_GCC_WARNING_DISABLE(-Wformat)
|
||||
DN_GCC_WARNING_DISABLE(-Wformat-extra-args)
|
||||
int size = DN_SNPrintF(dest,
|
||||
DN_CAST(int)dest_size,
|
||||
"%04u-%02u-%02uT%02u:%02u:%02u" // date
|
||||
"%S" // colour
|
||||
"%S" // bold
|
||||
"%S" // type
|
||||
"%.*s" // type padding
|
||||
"%S" // reset
|
||||
" %S" // file name
|
||||
":%05I32u " // line number
|
||||
,
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
date.hour,
|
||||
date.minute,
|
||||
date.second,
|
||||
colour_esc, // colour
|
||||
bold_esc, // bold
|
||||
type_str8, // type
|
||||
DN_CAST(int) type_padding,
|
||||
"", // type padding
|
||||
reset_esc, // reset
|
||||
file_name, // file name
|
||||
call_site.line); // line number
|
||||
DN_GCC_WARNING_POP
|
||||
|
||||
static DN_USize max_header_length = 0;
|
||||
DN_USize size_no_ansi_codes = size - colour_esc.size - reset_esc.size - bold_esc.size;
|
||||
max_header_length = DN_Max(max_header_length, size_no_ansi_codes);
|
||||
DN_USize header_padding = max_header_length - size_no_ansi_codes;
|
||||
|
||||
DN_LOGPrefixSize result = {};
|
||||
result.size = size;
|
||||
result.padding = header_padding;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_LOG_SetEmitFromTypeFVFunc(DN_LOGEmitFromTypeFVFunc *print_func, void *user_data)
|
||||
{
|
||||
g_dn_base_log_emit_from_type_fv_func_ = print_func;
|
||||
g_dn_base_log_emit_from_type_fv_user_context_ = user_data;
|
||||
}
|
||||
|
||||
DN_API void DN_LOG_EmitFromType(DN_LOGTypeParam type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_LOGEmitFromTypeFVFunc *func = g_dn_base_log_emit_from_type_fv_func_;
|
||||
void *user_context = g_dn_base_log_emit_from_type_fv_user_context_;
|
||||
if (func) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
func(type, user_context, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_LOGTypeParam DN_LOG_MakeU32LogTypeParam(DN_LOGType type)
|
||||
{
|
||||
DN_LOGTypeParam result = {};
|
||||
result.is_u32_enum = true;
|
||||
result.u32 = type;
|
||||
return result;
|
||||
}
|
72
Base/dn_base_log.h
Normal file
72
Base/dn_base_log.h
Normal file
@ -0,0 +1,72 @@
|
||||
#if !defined(DN_BASE_LOG_H)
|
||||
#define DN_BASE_LOG_H
|
||||
|
||||
enum DN_LOGType
|
||||
{
|
||||
DN_LOGType_Debug,
|
||||
DN_LOGType_Info,
|
||||
DN_LOGType_Warning,
|
||||
DN_LOGType_Error,
|
||||
DN_LOGType_Count,
|
||||
};
|
||||
|
||||
enum DN_LOGBold
|
||||
{
|
||||
DN_LOGBold_No,
|
||||
DN_LOGBold_Yes,
|
||||
};
|
||||
|
||||
struct DN_LOGStyle
|
||||
{
|
||||
DN_LOGBold bold;
|
||||
bool colour;
|
||||
DN_U8 r, g, b;
|
||||
};
|
||||
|
||||
struct DN_LOGTypeParam
|
||||
{
|
||||
bool is_u32_enum;
|
||||
DN_U32 u32;
|
||||
DN_Str8 str8;
|
||||
};
|
||||
|
||||
enum DN_LOGColourType
|
||||
{
|
||||
DN_LOGColourType_Fg,
|
||||
DN_LOGColourType_Bg,
|
||||
};
|
||||
|
||||
struct DN_LOGDate
|
||||
{
|
||||
DN_U16 year;
|
||||
DN_U8 month;
|
||||
DN_U8 day;
|
||||
|
||||
DN_U8 hour;
|
||||
DN_U8 minute;
|
||||
DN_U8 second;
|
||||
};
|
||||
|
||||
struct DN_LOGPrefixSize
|
||||
{
|
||||
DN_USize size;
|
||||
DN_USize padding;
|
||||
};
|
||||
|
||||
typedef void DN_LOGEmitFromTypeFVFunc(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
#define DN_LOG_ResetEscapeCode "\x1b[0m"
|
||||
#define DN_LOG_BoldEscapeCode "\x1b[1m"
|
||||
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType colour, DN_U8 r, DN_U8 g, DN_U8 b);
|
||||
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromU32(DN_LOGColourType colour, DN_U32 value);
|
||||
DN_API DN_LOGPrefixSize DN_LOG_MakePrefix (DN_LOGStyle style, DN_LOGTypeParam type, DN_CallSite call_site, DN_LOGDate date, char *dest, DN_USize dest_size);
|
||||
DN_API void DN_LOG_SetEmitFromTypeFVFunc (DN_LOGEmitFromTypeFVFunc *print_func, void *user_data);
|
||||
DN_API void DN_LOG_EmitFromType (DN_LOGTypeParam type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_LOGTypeParam DN_LOG_MakeU32LogTypeParam (DN_LOGType type);
|
||||
#define DN_LOG_DebugF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Debug), DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_LOG_InfoF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Info), DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_LOG_WarningF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Warning), DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_LOG_ErrorF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Error), DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#endif // !defined(DN_BASE_LOG_H)
|
553
Base/dn_base_mem.cpp
Normal file
553
Base/dn_base_mem.cpp
Normal file
@ -0,0 +1,553 @@
|
||||
#define DN_BASE_MEM_CPP
|
||||
|
||||
static DN_ArenaBlock *DN_Arena_BlockInitFromMemFuncs_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_ArenaMemFuncs mem_funcs)
|
||||
{
|
||||
DN_ArenaBlock *result = nullptr;
|
||||
switch (mem_funcs.type) {
|
||||
case DN_ArenaMemFuncType_Nil:
|
||||
break;
|
||||
|
||||
case DN_ArenaMemFuncType_Basic: {
|
||||
DN_AssertF(reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", reserve, DN_ARENA_HEADER_SIZE);
|
||||
result = DN_CAST(DN_ArenaBlock *) mem_funcs.basic_alloc(reserve);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
result->used = DN_ARENA_HEADER_SIZE;
|
||||
result->commit = reserve;
|
||||
result->reserve = reserve;
|
||||
} break;
|
||||
|
||||
case DN_ArenaMemFuncType_VMem: {
|
||||
DN_AssertF(mem_funcs.vmem_page_size, "Page size must be set to a non-zero, power of two value");
|
||||
DN_Assert(DN_IsPowerOfTwo(mem_funcs.vmem_page_size));
|
||||
|
||||
DN_USize const page_size = mem_funcs.vmem_page_size;
|
||||
DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE;
|
||||
DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE;
|
||||
real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size);
|
||||
real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve);
|
||||
DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%I64u < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve);
|
||||
|
||||
DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No;
|
||||
result = DN_CAST(DN_ArenaBlock *) mem_funcs.vmem_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
if (mem_commit == DN_MemCommit_No && !mem_funcs.vmem_commit(result, real_commit, DN_MemPage_ReadWrite)) {
|
||||
mem_funcs.vmem_release(result, real_reserve);
|
||||
return result;
|
||||
}
|
||||
|
||||
result->used = DN_ARENA_HEADER_SIZE;
|
||||
result->commit = real_commit;
|
||||
result->reserve = real_reserve;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (track_alloc && result)
|
||||
DN_Debug_TrackAlloc(result, result->reserve, alloc_can_leak);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DN_ArenaBlock *DN_Arena_BlockInitFlagsFromMemFuncs_(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs)
|
||||
{
|
||||
bool track_alloc = (flags & DN_ArenaFlags_NoAllocTrack) == 0;
|
||||
bool alloc_can_leak = flags & DN_ArenaFlags_AllocCanLeak;
|
||||
DN_ArenaBlock *result = DN_Arena_BlockInitFromMemFuncs_(reserve, commit, track_alloc, alloc_can_leak, mem_funcs);
|
||||
if (result && ((flags & DN_ArenaFlags_NoPoison) == 0))
|
||||
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *) result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_Arena_UpdateStatsOnNewBlock_(DN_Arena *arena, DN_ArenaBlock const *block)
|
||||
{
|
||||
DN_Assert(arena);
|
||||
if (block) {
|
||||
arena->stats.info.used += block->used;
|
||||
arena->stats.info.commit += block->commit;
|
||||
arena->stats.info.reserve += block->reserve;
|
||||
arena->stats.info.blocks += 1;
|
||||
|
||||
arena->stats.hwm.used = DN_Max(arena->stats.hwm.used, arena->stats.info.used);
|
||||
arena->stats.hwm.commit = DN_Max(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
arena->stats.hwm.reserve = DN_Max(arena->stats.hwm.reserve, arena->stats.info.reserve);
|
||||
arena->stats.hwm.blocks = DN_Max(arena->stats.hwm.blocks, arena->stats.info.blocks);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromBuffer(void *buffer, DN_USize size, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_Assert(buffer);
|
||||
DN_AssertF(DN_ARENA_HEADER_SIZE < size, "Buffer (%zu bytes) too small, need atleast %zu bytes to store arena metadata", size, DN_ARENA_HEADER_SIZE);
|
||||
DN_AssertF(DN_IsPowerOfTwo(size), "Buffer (%zu bytes) must be a power-of-two", size);
|
||||
|
||||
// NOTE: Init block
|
||||
DN_ArenaBlock *block = DN_CAST(DN_ArenaBlock *) buffer;
|
||||
block->commit = size;
|
||||
block->reserve = size;
|
||||
block->used = DN_ARENA_HEADER_SIZE;
|
||||
if (block && ((flags & DN_ArenaFlags_NoPoison) == 0))
|
||||
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *) block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE);
|
||||
|
||||
DN_Arena result = {};
|
||||
result.flags = flags | DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_UserBuffer;
|
||||
result.curr = block;
|
||||
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromMemFuncs(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs)
|
||||
{
|
||||
DN_Arena result = {};
|
||||
result.flags = flags;
|
||||
result.mem_funcs = mem_funcs;
|
||||
result.flags |= DN_ArenaFlags_MemFuncs;
|
||||
result.curr = DN_Arena_BlockInitFlagsFromMemFuncs_(reserve, commit, flags, mem_funcs);
|
||||
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_Arena_BlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
|
||||
{
|
||||
DN_USize release_size = block->reserve;
|
||||
if (DN_Bit_IsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
|
||||
DN_Debug_TrackDealloc(block);
|
||||
DN_ASAN_UnpoisonMemoryRegion(block, block->commit);
|
||||
if (arena->flags & DN_ArenaFlags_MemFuncs) {
|
||||
if (arena->mem_funcs.type == DN_ArenaMemFuncType_Basic)
|
||||
arena->mem_funcs.basic_dealloc(block);
|
||||
else
|
||||
arena->mem_funcs.vmem_release(block, release_size);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Deinit(DN_Arena *arena)
|
||||
{
|
||||
for (DN_ArenaBlock *block = arena ? arena->curr : nullptr; block;) {
|
||||
DN_ArenaBlock *block_to_free = block;
|
||||
block = block->prev;
|
||||
DN_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
if (arena)
|
||||
*arena = {};
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_CommitTo(DN_Arena *arena, DN_U64 pos)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
if (pos <= curr->commit)
|
||||
return true;
|
||||
|
||||
DN_U64 real_pos = pos;
|
||||
if (!DN_Check(pos <= curr->reserve))
|
||||
real_pos = curr->reserve;
|
||||
|
||||
DN_Assert(arena->mem_funcs.vmem_page_size);
|
||||
DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, arena->mem_funcs.vmem_page_size);
|
||||
DN_USize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
|
||||
if (!arena->mem_funcs.vmem_commit(commit_ptr, commit_size, DN_MemPage_ReadWrite))
|
||||
return false;
|
||||
|
||||
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
|
||||
if (poison)
|
||||
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
|
||||
curr->commit = end_commit;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_Commit(DN_Arena *arena, DN_U64 size)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
DN_U64 pos = DN_Min(arena->curr->reserve, arena->curr->commit + size);
|
||||
bool result = DN_Arena_CommitTo(arena, pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_Grow(DN_Arena *arena, DN_U64 reserve, DN_U64 commit)
|
||||
{
|
||||
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
DN_ArenaBlock *new_block = DN_Arena_BlockInitFlagsFromMemFuncs_(reserve, commit, arena->flags, arena->mem_funcs);
|
||||
if (new_block) {
|
||||
result = true;
|
||||
new_block->prev = arena->curr;
|
||||
arena->curr = new_block;
|
||||
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
|
||||
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_Alloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
if (!arena->curr) {
|
||||
arena->curr = DN_Arena_BlockInitFlagsFromMemFuncs_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, arena->flags, arena->mem_funcs);
|
||||
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
}
|
||||
|
||||
if (!arena->curr)
|
||||
return nullptr;
|
||||
|
||||
try_alloc_again:
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
|
||||
uint8_t real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align;
|
||||
DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0);
|
||||
DN_U64 end_pos = offset_pos + size;
|
||||
DN_U64 alloc_size = end_pos - curr->used;
|
||||
|
||||
if (end_pos > curr->reserve) {
|
||||
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
|
||||
return nullptr;
|
||||
DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE);
|
||||
DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE);
|
||||
if (!DN_Arena_Grow(arena, new_reserve, new_commit))
|
||||
return nullptr;
|
||||
goto try_alloc_again;
|
||||
}
|
||||
|
||||
DN_USize prev_arena_commit = curr->commit;
|
||||
if (end_pos > curr->commit) {
|
||||
DN_Assert(arena->mem_funcs.vmem_page_size);
|
||||
DN_Assert(arena->mem_funcs.type == DN_ArenaMemFuncType_VMem);
|
||||
DN_Assert((arena->flags & DN_ArenaFlags_UserBuffer) == 0);
|
||||
DN_USize end_commit = DN_AlignUpPowerOfTwo(end_pos, arena->mem_funcs.vmem_page_size);
|
||||
DN_USize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
|
||||
if (!arena->mem_funcs.vmem_commit(commit_ptr, commit_size, DN_MemPage_ReadWrite))
|
||||
return nullptr;
|
||||
if (poison)
|
||||
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
curr->commit = end_commit;
|
||||
arena->stats.info.commit += commit_size;
|
||||
arena->stats.hwm.commit = DN_Max(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
}
|
||||
|
||||
void *result = DN_CAST(char *) curr + offset_pos;
|
||||
curr->used += alloc_size;
|
||||
arena->stats.info.used += alloc_size;
|
||||
arena->stats.hwm.used = DN_Max(arena->stats.hwm.used, arena->stats.info.used);
|
||||
DN_ASAN_UnpoisonMemoryRegion(result, size);
|
||||
|
||||
if (zero_mem == DN_ZeroMem_Yes) {
|
||||
DN_USize reused_bytes = DN_Min(prev_arena_commit - offset_pos, size);
|
||||
DN_Memset(result, 0, reused_bytes);
|
||||
}
|
||||
|
||||
DN_Assert(arena->stats.hwm.used >= arena->stats.info.used);
|
||||
DN_Assert(arena->stats.hwm.commit >= arena->stats.info.commit);
|
||||
DN_Assert(arena->stats.hwm.reserve >= arena->stats.info.reserve);
|
||||
DN_Assert(arena->stats.hwm.blocks >= arena->stats.info.blocks);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_AllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_ArenaFlags prev_flags = arena->flags;
|
||||
arena->flags |= (DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoPoison);
|
||||
void *memory = DN_Arena_Alloc(arena, size, align, zero_mem);
|
||||
arena->flags = prev_flags;
|
||||
return memory;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_Copy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align)
|
||||
{
|
||||
if (!arena || !data || size == 0)
|
||||
return nullptr;
|
||||
void *result = DN_Arena_Alloc(arena, size, align, DN_ZeroMem_No);
|
||||
if (result)
|
||||
DN_Memcpy(result, data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_PopTo(DN_Arena *arena, DN_U64 init_used)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return;
|
||||
DN_U64 used = DN_Max(DN_ARENA_HEADER_SIZE, init_used);
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
while (curr->reserve_sum >= used) {
|
||||
DN_ArenaBlock *block_to_free = curr;
|
||||
arena->stats.info.used -= block_to_free->used;
|
||||
arena->stats.info.commit -= block_to_free->commit;
|
||||
arena->stats.info.reserve -= block_to_free->reserve;
|
||||
arena->stats.info.blocks -= 1;
|
||||
if (arena->flags & DN_ArenaFlags_UserBuffer)
|
||||
break;
|
||||
curr = curr->prev;
|
||||
DN_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
|
||||
arena->stats.info.used -= curr->used;
|
||||
arena->curr = curr;
|
||||
curr->used = used - curr->reserve_sum;
|
||||
char *poison_ptr = (char *)curr + DN_AlignUpPowerOfTwo(curr->used, DN_ASAN_POISON_ALIGNMENT);
|
||||
DN_USize poison_size = ((char *)curr + curr->commit) - poison_ptr;
|
||||
DN_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
|
||||
arena->stats.info.used += curr->used;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Pop(DN_Arena *arena, DN_U64 amount)
|
||||
{
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
DN_USize used_sum = curr->reserve_sum + curr->used;
|
||||
if (!DN_Check(amount <= used_sum))
|
||||
amount = used_sum;
|
||||
DN_USize pop_to = used_sum - amount;
|
||||
DN_Arena_PopTo(arena, pop_to);
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_Arena_Pos(DN_Arena const *arena)
|
||||
{
|
||||
DN_U64 result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Clear(DN_Arena *arena)
|
||||
{
|
||||
DN_Arena_PopTo(arena, 0);
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_OwnsPtr(DN_Arena const *arena, void *ptr)
|
||||
{
|
||||
bool result = false;
|
||||
uintptr_t uint_ptr = DN_CAST(uintptr_t) ptr;
|
||||
for (DN_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
|
||||
uintptr_t begin = DN_CAST(uintptr_t) block + DN_ARENA_HEADER_SIZE;
|
||||
uintptr_t end = begin + block->reserve;
|
||||
result = uint_ptr >= begin && uint_ptr <= end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumStatsArray(DN_ArenaStats const *array, DN_USize size)
|
||||
{
|
||||
DN_ArenaStats result = {};
|
||||
DN_ForItSize(it, DN_ArenaStats const, array, size)
|
||||
{
|
||||
DN_ArenaStats stats = *it.data;
|
||||
result.info.used += stats.info.used;
|
||||
result.info.commit += stats.info.commit;
|
||||
result.info.reserve += stats.info.reserve;
|
||||
result.info.blocks += stats.info.blocks;
|
||||
|
||||
result.hwm.used = DN_Max(result.hwm.used, result.info.used);
|
||||
result.hwm.commit = DN_Max(result.hwm.commit, result.info.commit);
|
||||
result.hwm.reserve = DN_Max(result.hwm.reserve, result.info.reserve);
|
||||
result.hwm.blocks = DN_Max(result.hwm.blocks, result.info.blocks);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumStats(DN_ArenaStats lhs, DN_ArenaStats rhs)
|
||||
{
|
||||
DN_ArenaStats array[] = {lhs, rhs};
|
||||
DN_ArenaStats result = DN_Arena_SumStatsArray(array, DN_ArrayCountU(array));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats(DN_Arena const *array, DN_USize size)
|
||||
{
|
||||
DN_ArenaStats result = {};
|
||||
for (DN_USize index = 0; index < size; index++) {
|
||||
DN_Arena const *arena = array + index;
|
||||
result = DN_Arena_SumStats(result, arena->stats);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin(DN_Arena *arena)
|
||||
{
|
||||
DN_ArenaTempMem result = {};
|
||||
if (arena) {
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
result = {arena, curr ? curr->reserve_sum + curr->used : 0};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
DN_API void DN_Arena_TempMemEnd(DN_ArenaTempMem mem)
|
||||
{
|
||||
DN_Arena_PopTo(mem.arena, mem.used_sum);
|
||||
};
|
||||
|
||||
DN_ArenaTempMemScope::DN_ArenaTempMemScope(DN_Arena *arena)
|
||||
{
|
||||
mem = DN_Arena_TempMemBegin(arena);
|
||||
}
|
||||
|
||||
DN_ArenaTempMemScope::~DN_ArenaTempMemScope()
|
||||
{
|
||||
DN_Arena_TempMemEnd(mem);
|
||||
}
|
||||
|
||||
// NOTE: DN_Pool ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Pool DN_Pool_Init(DN_Arena *arena, uint8_t align)
|
||||
{
|
||||
DN_Pool result = {};
|
||||
if (arena) {
|
||||
result.arena = arena;
|
||||
result.align = align ? align : DN_POOL_DEFAULT_ALIGN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_Pool_IsValid(DN_Pool const *pool)
|
||||
{
|
||||
bool result = pool && pool->arena && pool->align;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Pool_Alloc(DN_Pool *pool, DN_USize size)
|
||||
{
|
||||
void *result = nullptr;
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
DN_USize const required_size = sizeof(DN_PoolSlot) + pool->align + size;
|
||||
DN_USize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. DN_PoolSlotSize_32B
|
||||
DN_USize slot_index = 0;
|
||||
if (required_size > 32) {
|
||||
// NOTE: Round up if not PoT as the low bits are set.
|
||||
DN_USize dist_to_next_msb = DN_CountLeadingZerosU64(required_size) + 1;
|
||||
dist_to_next_msb -= DN_CAST(DN_USize)(!DN_IsPowerOfTwo(required_size));
|
||||
|
||||
DN_USize const register_size = sizeof(DN_USize) * 8;
|
||||
DN_Assert(register_size >= dist_to_next_msb + size_to_slot_offset);
|
||||
slot_index = register_size - dist_to_next_msb - size_to_slot_offset;
|
||||
}
|
||||
|
||||
if (!DN_CheckF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size"))
|
||||
return result;
|
||||
|
||||
DN_USize slot_size_in_bytes = 1ULL << (slot_index + size_to_slot_offset);
|
||||
DN_Assert(required_size <= (slot_size_in_bytes << 0));
|
||||
DN_Assert(required_size >= (slot_size_in_bytes >> 1));
|
||||
|
||||
DN_PoolSlot *slot = nullptr;
|
||||
if (pool->slots[slot_index]) {
|
||||
slot = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot->next;
|
||||
DN_Memset(slot->data, 0, size);
|
||||
DN_Assert(DN_IsPowerOfTwoAligned(slot->data, pool->align));
|
||||
} else {
|
||||
void *bytes = DN_Arena_Alloc(pool->arena, slot_size_in_bytes, alignof(DN_PoolSlot), DN_ZeroMem_Yes);
|
||||
slot = DN_CAST(DN_PoolSlot *) bytes;
|
||||
|
||||
// NOTE: The raw pointer is round up to the next 'pool->align'-ed
|
||||
// address ensuring at least 1 byte of padding between the raw pointer
|
||||
// and the pointer given to the user and that the user pointer is
|
||||
// aligned to the pool's alignment.
|
||||
//
|
||||
// This allows us to smuggle 1 byte behind the user pointer that has
|
||||
// the offset to the original pointer.
|
||||
slot->data = DN_CAST(void *) DN_AlignDownPowerOfTwo(DN_CAST(uintptr_t) slot + sizeof(DN_PoolSlot) + pool->align, pool->align);
|
||||
|
||||
uintptr_t offset_to_original_ptr = DN_CAST(uintptr_t) slot->data - DN_CAST(uintptr_t) bytes;
|
||||
DN_Assert(slot->data > bytes);
|
||||
DN_Assert(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
|
||||
|
||||
// NOTE: Store the offset to the original pointer behind the user's
|
||||
// pointer.
|
||||
char *offset_to_original_storage = DN_CAST(char *) slot->data - 1;
|
||||
DN_Memcpy(offset_to_original_storage, &offset_to_original_ptr, 1);
|
||||
}
|
||||
|
||||
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
|
||||
// pointer gets returned which free list to return the pointer to.
|
||||
result = slot->data;
|
||||
slot->next = DN_CAST(DN_PoolSlot *) slot_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8FV(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
DN_USize size_required = DN_CStr8_FVSize(fmt, args);
|
||||
result.data = DN_CAST(char *) DN_Pool_Alloc(pool, size_required + 1);
|
||||
if (result.data) {
|
||||
result.size = size_required;
|
||||
DN_VSNPrintF(result.data, DN_CAST(int)(result.size + 1), fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8F(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Pool_AllocStr8FV(pool, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8Copy(DN_Pool *pool, DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
if (!DN_Str8_HasData(string))
|
||||
return result;
|
||||
|
||||
char *data = DN_CAST(char *) DN_Pool_Alloc(pool, string.size + 1);
|
||||
if (!data)
|
||||
return result;
|
||||
|
||||
DN_Memcpy(data, string.data, string.size);
|
||||
data[string.size] = 0;
|
||||
result = DN_Str8_Init(data, string.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Pool_Dealloc(DN_Pool *pool, void *ptr)
|
||||
{
|
||||
if (!DN_Pool_IsValid(pool) || !ptr)
|
||||
return;
|
||||
|
||||
DN_Assert(DN_Arena_OwnsPtr(pool->arena, ptr));
|
||||
|
||||
char const *one_byte_behind_ptr = DN_CAST(char *) ptr - 1;
|
||||
DN_USize offset_to_original_ptr = 0;
|
||||
DN_Memcpy(&offset_to_original_ptr, one_byte_behind_ptr, 1);
|
||||
DN_Assert(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
|
||||
|
||||
char *original_ptr = DN_CAST(char *) ptr - offset_to_original_ptr;
|
||||
DN_PoolSlot *slot = DN_CAST(DN_PoolSlot *) original_ptr;
|
||||
DN_PoolSlotSize slot_index = DN_CAST(DN_PoolSlotSize)(DN_CAST(uintptr_t) slot->next);
|
||||
DN_Assert(slot_index < DN_PoolSlotSize_Count);
|
||||
|
||||
slot->next = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot;
|
||||
}
|
||||
|
||||
DN_API void *DN_Pool_Copy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align)
|
||||
{
|
||||
if (!pool || !data || size == 0)
|
||||
return nullptr;
|
||||
|
||||
// TODO: Hmm should align be part of the alloc interface in general? I'm not going to worry
|
||||
// about this until we crash because of misalignment.
|
||||
DN_Assert(pool->align >= align);
|
||||
|
||||
void *result = DN_Pool_Alloc(pool, size);
|
||||
if (result)
|
||||
DN_Memcpy(result, data, size);
|
||||
return result;
|
||||
}
|
253
Base/dn_base_mem.h
Normal file
253
Base/dn_base_mem.h
Normal file
@ -0,0 +1,253 @@
|
||||
#if !defined(DN_BASE_MEM_H)
|
||||
#define DN_BASE_MEM_H
|
||||
|
||||
#include "../dn_clangd.h"
|
||||
|
||||
enum DN_MemCommit
|
||||
{
|
||||
DN_MemCommit_No,
|
||||
DN_MemCommit_Yes,
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_MemPage;
|
||||
enum DN_MemPage_
|
||||
{
|
||||
// Exception on read/write with a page. This flag overrides the read/write
|
||||
// access.
|
||||
DN_MemPage_NoAccess = 1 << 0,
|
||||
|
||||
DN_MemPage_Read = 1 << 1, // Only read permitted on the page.
|
||||
|
||||
// Only write permitted on the page. On Windows this is not supported and
|
||||
// will be promoted to read+write permissions.
|
||||
DN_MemPage_Write = 1 << 2,
|
||||
|
||||
DN_MemPage_ReadWrite = DN_MemPage_Read | DN_MemPage_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 DN_MemPage_NoAccess.
|
||||
// This flag must only be used in DN_Mem_Protect
|
||||
DN_MemPage_Guard = 1 << 3,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
DN_MemPage_AllocRecordLeakPermitted = 1 << 4,
|
||||
|
||||
// If leak tracing is enabled this flag will prevent any allocation record
|
||||
// from being created in the allocation table at all. If this flag is
|
||||
// enabled, 'OSMemPage_AllocRecordLeakPermitted' has no effect since the
|
||||
// record will never be created.
|
||||
DN_MemPage_NoAllocRecordEntry = 1 << 5,
|
||||
|
||||
// [INTERNAL] Do not use. All flags together do not constitute a correct
|
||||
// configuration of pages.
|
||||
DN_MemPage_All = DN_MemPage_NoAccess |
|
||||
DN_MemPage_ReadWrite |
|
||||
DN_MemPage_Guard |
|
||||
DN_MemPage_AllocRecordLeakPermitted |
|
||||
DN_MemPage_NoAllocRecordEntry,
|
||||
};
|
||||
|
||||
#if !defined(DN_ARENA_RESERVE_SIZE)
|
||||
#define DN_ARENA_RESERVE_SIZE DN_Megabytes(64)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_ARENA_COMMIT_SIZE)
|
||||
#define DN_ARENA_COMMIT_SIZE DN_Kilobytes(64)
|
||||
#endif
|
||||
|
||||
struct DN_ArenaBlock
|
||||
{
|
||||
DN_ArenaBlock *prev;
|
||||
DN_U64 used;
|
||||
DN_U64 commit;
|
||||
DN_U64 reserve;
|
||||
DN_U64 reserve_sum;
|
||||
};
|
||||
|
||||
typedef uint32_t DN_ArenaFlags;
|
||||
|
||||
enum DN_ArenaFlags_
|
||||
{
|
||||
DN_ArenaFlags_Nil = 0,
|
||||
DN_ArenaFlags_NoGrow = 1 << 0,
|
||||
DN_ArenaFlags_NoPoison = 1 << 1,
|
||||
DN_ArenaFlags_NoAllocTrack = 1 << 2,
|
||||
DN_ArenaFlags_AllocCanLeak = 1 << 3,
|
||||
|
||||
// NOTE: Internal flags. Do not use
|
||||
DN_ArenaFlags_UserBuffer = 1 << 4,
|
||||
DN_ArenaFlags_MemFuncs = 1 << 5,
|
||||
};
|
||||
|
||||
struct DN_ArenaInfo
|
||||
{
|
||||
DN_U64 used;
|
||||
DN_U64 commit;
|
||||
DN_U64 reserve;
|
||||
DN_U64 blocks;
|
||||
};
|
||||
|
||||
struct DN_ArenaStats
|
||||
{
|
||||
DN_ArenaInfo info;
|
||||
DN_ArenaInfo hwm;
|
||||
};
|
||||
|
||||
enum DN_ArenaMemFuncType
|
||||
{
|
||||
DN_ArenaMemFuncType_Nil,
|
||||
DN_ArenaMemFuncType_Basic,
|
||||
DN_ArenaMemFuncType_VMem,
|
||||
};
|
||||
|
||||
typedef void *(DN_ArenaMemBasicAllocFunc)(DN_USize size);
|
||||
typedef void (DN_ArenaMemBasicDeallocFunc)(void *ptr);
|
||||
typedef void *(DN_ArenaMemVMemReserveFunc)(DN_USize size, DN_MemCommit commit, DN_MemPage page_flags);
|
||||
typedef bool (DN_ArenaMemVMemCommitFunc)(void *ptr, DN_USize size, DN_U32 page_flags);
|
||||
typedef void (DN_ArenaMemVMemReleaseFunc)(void *ptr, DN_USize size);
|
||||
struct DN_ArenaMemFuncs
|
||||
{
|
||||
DN_ArenaMemFuncType type;
|
||||
DN_ArenaMemBasicAllocFunc *basic_alloc;
|
||||
DN_ArenaMemBasicDeallocFunc *basic_dealloc;
|
||||
|
||||
DN_U32 vmem_page_size;
|
||||
DN_ArenaMemVMemReserveFunc *vmem_reserve;
|
||||
DN_ArenaMemVMemCommitFunc *vmem_commit;
|
||||
DN_ArenaMemVMemReleaseFunc *vmem_release;
|
||||
};
|
||||
|
||||
struct DN_Arena
|
||||
{
|
||||
DN_ArenaMemFuncs mem_funcs;
|
||||
DN_ArenaBlock *curr;
|
||||
DN_ArenaStats stats;
|
||||
DN_ArenaFlags flags;
|
||||
DN_Str8 label;
|
||||
DN_Arena *prev, *next;
|
||||
};
|
||||
|
||||
struct DN_ArenaTempMem
|
||||
{
|
||||
DN_Arena *arena;
|
||||
DN_U64 used_sum;
|
||||
};
|
||||
|
||||
struct DN_ArenaTempMemScope
|
||||
{
|
||||
DN_ArenaTempMemScope(DN_Arena *arena);
|
||||
~DN_ArenaTempMemScope();
|
||||
DN_ArenaTempMem mem;
|
||||
};
|
||||
|
||||
DN_USize const DN_ARENA_HEADER_SIZE = DN_AlignUpPowerOfTwo(sizeof(DN_Arena), 64);
|
||||
|
||||
// NOTE: DN_Arena //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Arena DN_Arena_InitFromBuffer (void *buffer, DN_USize size, DN_ArenaFlags flags);
|
||||
DN_API DN_Arena DN_Arena_InitFromMemFuncs (DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs);
|
||||
DN_API void DN_Arena_Deinit (DN_Arena *arena);
|
||||
DN_API bool DN_Arena_Commit (DN_Arena *arena, DN_U64 size);
|
||||
DN_API bool DN_Arena_CommitTo (DN_Arena *arena, DN_U64 pos);
|
||||
DN_API bool DN_Arena_Grow (DN_Arena *arena, DN_U64 reserve, DN_U64 commit);
|
||||
DN_API void * DN_Arena_Alloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
|
||||
DN_API void * DN_Arena_AllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
|
||||
DN_API void * DN_Arena_Copy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align);
|
||||
DN_API void DN_Arena_PopTo (DN_Arena *arena, DN_U64 init_used);
|
||||
DN_API void DN_Arena_Pop (DN_Arena *arena, DN_U64 amount);
|
||||
DN_API DN_U64 DN_Arena_Pos (DN_Arena const *arena);
|
||||
DN_API void DN_Arena_Clear (DN_Arena *arena);
|
||||
DN_API bool DN_Arena_OwnsPtr (DN_Arena const *arena, void *ptr);
|
||||
DN_API DN_ArenaStats DN_Arena_SumStatsArray (DN_ArenaStats const *array, DN_USize size);
|
||||
DN_API DN_ArenaStats DN_Arena_SumStats (DN_ArenaStats lhs, DN_ArenaStats rhs);
|
||||
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats (DN_Arena const *array, DN_USize size);
|
||||
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin (DN_Arena *arena);
|
||||
DN_API void DN_Arena_TempMemEnd (DN_ArenaTempMem mem);
|
||||
#define DN_Arena_New_Frame(T, zero_mem) (T *)DN_Arena_Alloc(DN_OS_TLSGet()->frame_arena, sizeof(T), alignof(T), zero_mem)
|
||||
#define DN_Arena_New(arena, T, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
|
||||
#define DN_Arena_NewArray(arena, T, count, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T) * (count), alignof(T), zero_mem)
|
||||
#define DN_Arena_NewCopy(arena, T, src) (T *)DN_Arena_Copy (arena, (src), sizeof(T), alignof(T))
|
||||
#define DN_Arena_NewArrayCopy(arena, T, src, count) (T *)DN_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
#if !defined(DN_POOL_DEFAULT_ALIGN)
|
||||
#define DN_POOL_DEFAULT_ALIGN 16
|
||||
#endif
|
||||
|
||||
struct DN_PoolSlot
|
||||
{
|
||||
void *data;
|
||||
DN_PoolSlot *next;
|
||||
};
|
||||
|
||||
enum DN_PoolSlotSize
|
||||
{
|
||||
DN_PoolSlotSize_32B,
|
||||
DN_PoolSlotSize_64B,
|
||||
DN_PoolSlotSize_128B,
|
||||
DN_PoolSlotSize_256B,
|
||||
DN_PoolSlotSize_512B,
|
||||
DN_PoolSlotSize_1KiB,
|
||||
DN_PoolSlotSize_2KiB,
|
||||
DN_PoolSlotSize_4KiB,
|
||||
DN_PoolSlotSize_8KiB,
|
||||
DN_PoolSlotSize_16KiB,
|
||||
DN_PoolSlotSize_32KiB,
|
||||
DN_PoolSlotSize_64KiB,
|
||||
DN_PoolSlotSize_128KiB,
|
||||
DN_PoolSlotSize_256KiB,
|
||||
DN_PoolSlotSize_512KiB,
|
||||
DN_PoolSlotSize_1MiB,
|
||||
DN_PoolSlotSize_2MiB,
|
||||
DN_PoolSlotSize_4MiB,
|
||||
DN_PoolSlotSize_8MiB,
|
||||
DN_PoolSlotSize_16MiB,
|
||||
DN_PoolSlotSize_32MiB,
|
||||
DN_PoolSlotSize_64MiB,
|
||||
DN_PoolSlotSize_128MiB,
|
||||
DN_PoolSlotSize_256MiB,
|
||||
DN_PoolSlotSize_512MiB,
|
||||
DN_PoolSlotSize_1GiB,
|
||||
DN_PoolSlotSize_2GiB,
|
||||
DN_PoolSlotSize_4GiB,
|
||||
DN_PoolSlotSize_8GiB,
|
||||
DN_PoolSlotSize_16GiB,
|
||||
DN_PoolSlotSize_32GiB,
|
||||
DN_PoolSlotSize_Count,
|
||||
};
|
||||
|
||||
struct DN_Pool
|
||||
{
|
||||
DN_Arena *arena;
|
||||
DN_PoolSlot *slots[DN_PoolSlotSize_Count];
|
||||
uint8_t align;
|
||||
};
|
||||
|
||||
// NOTE: DN_Pool ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Pool DN_Pool_Init (DN_Arena *arena, uint8_t align);
|
||||
DN_API bool DN_Pool_IsValid (DN_Pool const *pool);
|
||||
DN_API void * DN_Pool_Alloc (DN_Pool *pool, DN_USize size);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8FV (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8F (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8Copy (DN_Pool *pool, DN_Str8 string);
|
||||
DN_API void DN_Pool_Dealloc (DN_Pool *pool, void *ptr);
|
||||
DN_API void * DN_Pool_Copy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align);
|
||||
|
||||
#define DN_Pool_New(pool, T) (T *)DN_Pool_Alloc(pool, sizeof(T))
|
||||
#define DN_Pool_NewArray(pool, T, count) (T *)DN_Pool_Alloc(pool, count * sizeof(T))
|
||||
#define DN_Pool_NewCopy(arena, T, src) (T *)DN_Pool_Copy (arena, (src), sizeof(T), alignof(T))
|
||||
#define DN_Pool_NewArrayCopy(arena, T, src, count) (T *)DN_Pool_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
// NOTE: DN_Debug //////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING) && !defined(DN_FREESTANDING)
|
||||
DN_API void DN_Debug_TrackAlloc (void *ptr, DN_USize size, bool alloc_can_leak);
|
||||
DN_API void DN_Debug_TrackDealloc(void *ptr);
|
||||
DN_API void DN_Debug_DumpLeaks ();
|
||||
#else
|
||||
#define DN_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
|
||||
#define DN_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
|
||||
#define DN_Debug_DumpLeaks() do { } while (0)
|
||||
#endif
|
||||
#endif // !defined(DN_BASE_MEM_H)
|
46
Base/dn_base_os.h
Normal file
46
Base/dn_base_os.h
Normal file
@ -0,0 +1,46 @@
|
||||
#if !defined(DN_BASE_OS_H)
|
||||
#define DN_BASE_OS_H
|
||||
|
||||
// NOTE: OS primitives that the OS layer can provide for the base layer but is optional.
|
||||
|
||||
struct DN_StackTraceFrame
|
||||
{
|
||||
DN_U64 address;
|
||||
DN_U64 line_number;
|
||||
DN_Str8 file_name;
|
||||
DN_Str8 function_name;
|
||||
};
|
||||
|
||||
struct DN_StackTraceRawFrame
|
||||
{
|
||||
void *process;
|
||||
DN_U64 base_addr;
|
||||
};
|
||||
|
||||
struct DN_StackTraceWalkResult
|
||||
{
|
||||
void *process; // [Internal] Windows handle to the process
|
||||
DN_U64 *base_addr; // The addresses of the functions in the stack trace
|
||||
DN_U16 size; // The number of `base_addr`'s stored from the walk
|
||||
};
|
||||
|
||||
struct DN_StackTraceWalkResultIterator
|
||||
{
|
||||
DN_StackTraceRawFrame raw_frame;
|
||||
DN_U16 index;
|
||||
};
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
|
||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
|
||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
|
||||
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames (struct DN_Arena *arena, DN_U16 limit);
|
||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
||||
DN_API void DN_StackTrace_Print (DN_U16 limit);
|
||||
DN_API void DN_StackTrace_ReloadSymbols ();
|
||||
|
||||
|
||||
#endif
|
||||
|
1183
Base/dn_base_string.cpp
Normal file
1183
Base/dn_base_string.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,30 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#if !defined(DN_BASE_STRING_H)
|
||||
#define DN_BASE_STRING_H
|
||||
|
||||
#if defined(DN_USE_STD_PRINTF)
|
||||
#include <stdio.h>
|
||||
#define DN_SPrintF(...) sprintf(__VA_ARGS__)
|
||||
#define DN_SNPrintF(...) snprintf(__VA_ARGS__)
|
||||
#define DN_VSPrintF(...) vsprintf(__VA_ARGS__)
|
||||
#define DN_VSNPrintF(...) vsnprintf(__VA_ARGS__)
|
||||
#else
|
||||
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#define STB_SPRINTF_STATIC
|
||||
#endif
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(4505) // Unused function warning
|
||||
DN_GCC_WARNING_PUSH
|
||||
DN_GCC_WARNING_DISABLE(-Wunused-function)
|
||||
#include "../External/stb_sprintf.h"
|
||||
DN_GCC_WARNING_POP
|
||||
DN_MSVC_WARNING_POP
|
||||
|
||||
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
||||
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
||||
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
|
||||
#define DN_VSNPrintF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,133 +38,112 @@
|
||||
// \$$$$$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \__|\______|\__| \__| \______/
|
||||
//
|
||||
// dqn_string.h -- UTF8/16 string manipulation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$CSTR] DN_CStr8 -- C-string helpers
|
||||
// [$STR8] DN_Str8 -- Pointer and length strings
|
||||
// [$STRB] DN_Str8Builder -- Construct strings dynamically
|
||||
// [$FSTR] DN_FStr8 -- Fixed-size strings
|
||||
// [$CHAR] DN_Char -- Character ascii/digit.. helpers
|
||||
// [$UTFX] DN_UTF -- Unicode helpers
|
||||
// dn_base_string.h
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$STR8] DN_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Str8 //////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_Str8Link
|
||||
{
|
||||
DN_Str8 string; // The string
|
||||
DN_Str8Link *next; // The next string in the linked list
|
||||
DN_Str8Link *prev; // The prev string in the linked list
|
||||
};
|
||||
|
||||
struct DN_Str16 // A pointer and length style string that holds slices to UTF16 bytes.
|
||||
{
|
||||
wchar_t *data; // The UTF16 bytes of the string
|
||||
DN_USize size; // The number of characters in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
wchar_t const *begin() const { return data; } // Const begin iterator for range-for loops
|
||||
wchar_t const *end () const { return data + size; } // Const end iterator for range-for loops
|
||||
wchar_t *begin() { return data; } // Begin iterator for range-for loops
|
||||
wchar_t *end () { return data + size; } // End iterator for range-for loops
|
||||
#endif
|
||||
DN_Str8 string; // The string
|
||||
DN_Str8Link *next; // The next string in the linked list
|
||||
DN_Str8Link *prev; // The prev string in the linked list
|
||||
};
|
||||
|
||||
struct DN_Str8BinarySplitResult
|
||||
{
|
||||
DN_Str8 lhs;
|
||||
DN_Str8 rhs;
|
||||
DN_Str8 lhs;
|
||||
DN_Str8 rhs;
|
||||
};
|
||||
|
||||
struct DN_Str8FindResult
|
||||
{
|
||||
bool found; // True if string was found. If false, the subsequent fields below are not set.
|
||||
DN_USize index; // Index in the buffer where the found string starts
|
||||
DN_Str8 match; // Matching string in the buffer that was searched
|
||||
DN_Str8 match_to_end_of_buffer; // Substring containing the found string to the end of the buffer
|
||||
DN_Str8 after_match_to_end_of_buffer; // Substring starting after the found string to the end of the buffer
|
||||
DN_Str8 start_to_before_match; // Substring from the start of the buffer up until the found string, not including it
|
||||
bool found; // True if string was found. If false, the subsequent fields below are not set.
|
||||
DN_USize index; // Index in the buffer where the found string starts
|
||||
DN_Str8 match; // Matching string in the buffer that was searched
|
||||
DN_Str8 match_to_end_of_buffer; // Substring containing the found string to the end of the buffer
|
||||
DN_Str8 after_match_to_end_of_buffer; // Substring starting after the found string to the end of the buffer
|
||||
DN_Str8 start_to_before_match; // Substring from the start of the buffer up until the found string, not including it
|
||||
};
|
||||
|
||||
enum DN_Str8IsAll
|
||||
{
|
||||
DN_Str8IsAll_Digits,
|
||||
DN_Str8IsAll_Hex,
|
||||
DN_Str8IsAll_Digits,
|
||||
DN_Str8IsAll_Hex,
|
||||
};
|
||||
|
||||
enum DN_Str8EqCase
|
||||
{
|
||||
DN_Str8EqCase_Sensitive,
|
||||
DN_Str8EqCase_Insensitive,
|
||||
DN_Str8EqCase_Sensitive,
|
||||
DN_Str8EqCase_Insensitive,
|
||||
};
|
||||
|
||||
enum DN_Str8FindFlag
|
||||
{
|
||||
DN_Str8FindFlag_Digit = 1 << 0, // 0-9
|
||||
DN_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' '
|
||||
DN_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z
|
||||
DN_Str8FindFlag_Plus = 1 << 3, // +
|
||||
DN_Str8FindFlag_Minus = 1 << 4, // -
|
||||
DN_Str8FindFlag_AlphaNum = DN_Str8FindFlag_Alphabet | DN_Str8FindFlag_Digit,
|
||||
DN_Str8FindFlag_Digit = 1 << 0, // 0-9
|
||||
DN_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' '
|
||||
DN_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z
|
||||
DN_Str8FindFlag_Plus = 1 << 3, // +
|
||||
DN_Str8FindFlag_Minus = 1 << 4, // -
|
||||
DN_Str8FindFlag_AlphaNum = DN_Str8FindFlag_Alphabet | DN_Str8FindFlag_Digit,
|
||||
};
|
||||
|
||||
enum DN_Str8SplitIncludeEmptyStrings
|
||||
{
|
||||
DN_Str8SplitIncludeEmptyStrings_No,
|
||||
DN_Str8SplitIncludeEmptyStrings_Yes,
|
||||
DN_Str8SplitIncludeEmptyStrings_No,
|
||||
DN_Str8SplitIncludeEmptyStrings_Yes,
|
||||
};
|
||||
|
||||
struct DN_Str8ToU64Result
|
||||
{
|
||||
bool success;
|
||||
uint64_t value;
|
||||
bool success;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct DN_Str8ToI64Result
|
||||
{
|
||||
bool success;
|
||||
int64_t value;
|
||||
bool success;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
struct DN_Str8DotTruncateResult
|
||||
{
|
||||
bool truncated;
|
||||
bool truncated;
|
||||
DN_Str8 str8;
|
||||
};
|
||||
|
||||
// NOTE: [$FSTR] DN_FStr8 /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_FSTR8)
|
||||
template <DN_USize N> struct DN_FStr8
|
||||
template <DN_USize N>
|
||||
struct DN_FStr8
|
||||
{
|
||||
char data[N+1];
|
||||
DN_USize size;
|
||||
char data[N + 1];
|
||||
DN_USize size;
|
||||
|
||||
char *begin() { return data; }
|
||||
char *end () { return data + size; }
|
||||
char const *begin() const { return data; }
|
||||
char const *end () const { return data + size; }
|
||||
char *begin() { return data; }
|
||||
char *end() { return data + size; }
|
||||
char const *begin() const { return data; }
|
||||
char const *end() const { return data + size; }
|
||||
};
|
||||
#endif // !defined(DN_NO_FSTR8)
|
||||
|
||||
struct DN_Str8Builder
|
||||
{
|
||||
DN_Arena *arena; // Allocator to use to back the string list
|
||||
DN_Str8Link *head; // First string in the linked list of strings
|
||||
DN_Str8Link *tail; // Last string in the linked list of strings
|
||||
DN_USize string_size; // The size in bytes necessary to construct the current string
|
||||
DN_USize count; // The number of links in the linked list of strings
|
||||
DN_Arena *arena; // Allocator to use to back the string list
|
||||
DN_Str8Link *head; // First string in the linked list of strings
|
||||
DN_Str8Link *tail; // Last string in the linked list of strings
|
||||
DN_USize string_size; // The size in bytes necessary to construct the current string
|
||||
DN_USize count; // The number of links in the linked list of strings
|
||||
};
|
||||
|
||||
enum DN_Str8BuilderAdd
|
||||
{
|
||||
DN_Str8BuilderAdd_Append,
|
||||
DN_Str8BuilderAdd_Prepend,
|
||||
DN_Str8BuilderAdd_Append,
|
||||
DN_Str8BuilderAdd_Prepend,
|
||||
};
|
||||
|
||||
// NOTE: [$CSTR] DN_CStr8 /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_CStr8 //////////////////////////////////////////////////////////////////////////////////
|
||||
template <DN_USize N> constexpr DN_USize DN_CStr8_ArrayUCount (char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
template <DN_USize N> constexpr DN_USize DN_CStr8_ArrayICount (char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
DN_API DN_USize DN_CStr8_FSize (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
@ -147,7 +151,7 @@ DN_API DN_USize DN_CStr8_FVSize
|
||||
DN_API DN_USize DN_CStr8_Size (char const *a);
|
||||
DN_API DN_USize DN_CStr16_Size (wchar_t const *a);
|
||||
|
||||
// NOTE: [$STR6] DN_Str16 /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Str16 //////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_STR16(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1}
|
||||
#define DN_Str16_HasData(string) ((string).data && (string).size)
|
||||
|
||||
@ -156,7 +160,7 @@ DN_API bool operator==
|
||||
DN_API bool operator!= (DN_Str16 const &lhs, DN_Str16 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: [$STR8] DN_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_STR8(string) DN_Str8{(char *)(string), (sizeof(string) - 1)}
|
||||
#define DN_STR_FMT(string) (int)((string).size), (string).data
|
||||
#define DN_Str8_Init(data, size) DN_Str8{(char *)(data), (size_t)(size)}
|
||||
@ -166,17 +170,9 @@ DN_API DN_Str8 DN_Str8_InitCStr8
|
||||
DN_API bool DN_Str8_IsAll (DN_Str8 string, DN_Str8IsAll is_all);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_Str8_InitF_TLS(...) DN_Str8_InitF(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_Str8_InitF_Frame(...) DN_Str8_InitF(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_InitFV (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
#define DN_Str8_InitFV_TLS(...) DN_Str8_InitFV(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_Alloc (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
|
||||
#define DN_Str8_Alloc_TLS(...) DN_Str8_Alloc(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_CopyCString (DN_Arena *arena, char const *string, DN_USize size);
|
||||
#define DN_Str8_CopyCString_TLS(...) DN_Str8_CopyCString(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_Copy (DN_Arena *arena, DN_Str8 string);
|
||||
#define DN_Str8_Copy_TLS(...) DN_Str8_Copy(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_Str8_Copy_Frame(...) DN_Str8_Copy(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
|
||||
DN_API char * DN_Str8_End (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_Slice (DN_Str8 string, DN_USize offset, DN_USize size);
|
||||
@ -188,15 +184,12 @@ DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitL
|
||||
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitLast (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_USize DN_Str8_Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAlloc (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
#define DN_Str8_SplitAlloc_TLS(...) DN_Str8_SplitAlloc(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DN_API DN_Str8FindResult DN_Str8_FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case);
|
||||
DN_API DN_Str8FindResult DN_Str8_FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case);
|
||||
DN_API DN_Str8FindResult DN_Str8_Find (DN_Str8 string, uint32_t flags);
|
||||
DN_API DN_Str8 DN_Str8_Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
#define DN_Str8_Segment_TLS(...) DN_Str8_Segment(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
#define DN_Str8_ReverseSegment_TLS(...) DN_Str8_ReverseSegment(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DN_API bool DN_Str8_Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API bool DN_Str8_EqInsensitive (DN_Str8 lhs, DN_Str8 rhs);
|
||||
@ -210,6 +203,8 @@ DN_API DN_Str8 DN_Str8_TrimPrefix
|
||||
DN_API DN_Str8 DN_Str8_TrimHexPrefix (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API DN_Str8 DN_Str8_TrimAround (DN_Str8 string, DN_Str8 trim_string);
|
||||
DN_API DN_Str8 DN_Str8_TrimHeadWhitespace (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_TrimTailWhitespace (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_TrimWhitespaceAround (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_TrimByteOrderMark (DN_Str8 string);
|
||||
|
||||
@ -221,46 +216,30 @@ DN_API DN_Str8 DN_Str8_FileExtensio
|
||||
DN_API DN_Str8ToU64Result DN_Str8_ToU64 (DN_Str8 string, char separator);
|
||||
DN_API DN_Str8ToI64Result DN_Str8_ToI64 (DN_Str8 string, char separator);
|
||||
|
||||
#define DN_Str8_AppendF_TLS(...) DN_Str8_AppendF(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...);
|
||||
#define DN_Str8_FillF_TLS(count, ...) DN_Str8_FillF(DN_TLS_TopArena(), count, ##__VA_ARGS__)
|
||||
#define DN_Str8_FillF_Frame(count, ...) DN_Str8_FillF(DN_TLS_FrameArena(), count, ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API DN_Str8 DN_Str8_ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8_FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_Str8_Remove (DN_Str8 *string, DN_USize offset, DN_USize size);
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddle (DN_Arena *arena, DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
||||
#define DN_Str8_DotTruncateMiddle_Frame(...) DN_Str8_DotTruncateMiddle(DN_TLS_FrameArena(), ## __VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad);
|
||||
#define DN_Str8_PadNewLines_TLS(src, pad) DN_Str8_PadNewLines(DN_TLS_TopArena(), src, pad)
|
||||
|
||||
|
||||
#define DN_Str8_Lower_TLS(...) DN_Str8_Lower(DN_TLS_TopArena(), __VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_Lower (DN_Arena *arena, DN_Str8 string);
|
||||
#define DN_Str8_Upper_TLS(...) DN_Str8_Upper(DN_TLS_TopArena(), __VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8_Upper (DN_Arena *arena, DN_Str8 string);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DN_API bool operator== (DN_Str8 const &lhs, DN_Str8 const &rhs);
|
||||
DN_API bool operator!= (DN_Str8 const &lhs, DN_Str8 const &rhs);
|
||||
DN_API bool operator== (DN_Str8 const &lhs, DN_Str8 const &rhs);
|
||||
DN_API bool operator!= (DN_Str8 const &lhs, DN_Str8 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: [$STRB] DN_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8Builder DN_Str8Builder_Init (DN_Arena *arena);
|
||||
DN_API DN_Str8Builder DN_Str8Builder_Init_Frame () { return DN_Str8Builder_Init(DN_TLS_Get()->frame_arena); }
|
||||
#define DN_Str8Builder_Init_TLS() DN_Str8Builder_Init(DN_TLS_TopArena())
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRef_Frame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayRef(DN_TLS_Get()->frame_arena, strings, size); }
|
||||
#define DN_Str8Builder_InitArrayRef_TLS(...) DN_Str8Builder_InitArrayRef(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopy_Frame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayCopy(DN_TLS_Get()->frame_arena, strings, size); }
|
||||
#define DN_Str8Builder_InitArrayCopy_TLS(...) DN_Str8Builder_InitArrayCopy(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayRef (DN_Arena *arena, DN_Str8 const (&array)[N]);
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayRef_Frame (DN_Str8 const (&array)[N]) { return DN_Str8Builder_InitCArrayRef(DN_TLS_Get()->frame_arena, array); }
|
||||
#define DN_Str8Builder_InitCArrayRef_TLS(...) DN_Str8Builder_InitCArrayRef(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayCopy (DN_Arena *arena, DN_Str8 const (&array)[N]);
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayCopy_Frame (DN_Str8 const (&array)[N]) { return DN_Str8Builder_InitCArrayCopy(DN_TLS_Get()->frame_arena, array); }
|
||||
#define DN_Str8Builder_InitCArrayCopy_TLS(...) DN_Str8Builder_InitCArrayCopy(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DN_API bool DN_Str8Builder_AddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
|
||||
DN_API bool DN_Str8Builder_AddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
|
||||
@ -290,19 +269,11 @@ DN_API bool DN_Str8Builder_Prepe
|
||||
|
||||
DN_API bool DN_Str8Builder_Erase (DN_Str8Builder *builder, DN_Str8 string);
|
||||
DN_API DN_Str8Builder DN_Str8Builder_Copy (DN_Arena *arena, DN_Str8Builder const *builder);
|
||||
#define DN_Str8Builder_Copy_TLS(...) DN_Str8Builder_Copy(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_Str8Builder_Build (DN_Str8Builder const *builder, DN_Arena *arena);
|
||||
#define DN_Str8Builder_Build_TLS(...) DN_Str8Builder_Build(__VA_ARGS__, DN_TLS_TopArena())
|
||||
#define DN_Str8Builder_Build_Frame(...) DN_Str8Builder_Build(__VA_ARGS__, DN_TLS_FrameArena())
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildDelimited (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena);
|
||||
#define DN_Str8Builder_BuildDelimited_TLS(...) DN_Str8Builder_BuildDelimited(__VA_ARGS__, DN_TLS_TopArena())
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildCRT (DN_Str8Builder const *builder);
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSlice (DN_Str8Builder const *builder, DN_Arena *arena);
|
||||
#define DN_Str8Builder_BuildSlice_TLS(...) DN_Str8Builder_BuildSlice(__VA_ARGS__, DN_TLS_TopArena())
|
||||
DN_API void DN_Str8Builder_Print (DN_Str8Builder const *builder);
|
||||
DN_API void DN_Str8Builder_PrintLn (DN_Str8Builder const *builder);
|
||||
|
||||
// NOTE: [$FSTR] DN_FStr8 //////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_FSTR8)
|
||||
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitF (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitFV (char const *fmt, va_list args);
|
||||
@ -317,15 +288,15 @@ template <DN_USize N> bool DN_FStr8_Eq
|
||||
template <DN_USize N> bool DN_FStr8_EqStr8 (DN_FStr8<N> const *lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
|
||||
template <DN_USize N> bool DN_FStr8_EqInsensitive (DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs);
|
||||
template <DN_USize N> bool DN_FStr8_EqStr8Insensitive (DN_FStr8<N> const *lhs, DN_Str8 rhs);
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8 (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case);
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8Insensitive (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs);
|
||||
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
|
||||
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
|
||||
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
|
||||
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8 (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case);
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8Insensitive (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs);
|
||||
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
|
||||
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
|
||||
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
|
||||
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
|
||||
#endif // !defined(DN_NO_FSTR8)
|
||||
|
||||
// NOTE: [$CHAR] DN_Char //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Char ///////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_CharHexToU8
|
||||
{
|
||||
bool success;
|
||||
@ -343,195 +314,219 @@ DN_API char DN_Char_ToHexUncheck
|
||||
DN_API char DN_Char_ToLower (char ch);
|
||||
DN_API char DN_Char_ToUpper (char ch);
|
||||
|
||||
// NOTE: [$UTFX] DN_UTF ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_UTF ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API int DN_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint);
|
||||
DN_API int DN_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint);
|
||||
|
||||
// NOTE: [$STRB] DN_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayRef(DN_Arena *arena, DN_Str8 const (&array)[N])
|
||||
// NOTE: DN_Str8Builder ///////////////////////////////////////////////////////////////////////////
|
||||
template <DN_USize N>
|
||||
DN_Str8Builder DN_Str8Builder_InitCArrayRef(DN_Arena *arena, DN_Str8 const (&array)[N])
|
||||
{
|
||||
DN_Str8Builder result = DN_Str8Builder_InitArrayRef(arena, array, N);
|
||||
return result;
|
||||
DN_Str8Builder result = DN_Str8Builder_InitArrayRef(arena, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayCopy(DN_Arena *arena, DN_Str8 const (&array)[N])
|
||||
template <DN_USize N>
|
||||
DN_Str8Builder DN_Str8Builder_InitCArrayCopy(DN_Arena *arena, DN_Str8 const (&array)[N])
|
||||
{
|
||||
DN_Str8Builder result = DN_Str8Builder_InitArrayCopy(arena, array, N);
|
||||
return result;
|
||||
DN_Str8Builder result = DN_Str8Builder_InitArrayCopy(arena, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_Str8Builder_AddCArrayRef(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
|
||||
template <DN_USize N>
|
||||
bool DN_Str8Builder_AddCArrayRef(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
|
||||
{
|
||||
bool result = DN_Str8Builder_AddArrayRef(builder, array, N, add);
|
||||
return result;
|
||||
bool result = DN_Str8Builder_AddArrayRef(builder, array, N, add);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_Str8Builder_AddCArrayCopy(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
|
||||
template <DN_USize N>
|
||||
bool DN_Str8Builder_AddCArrayCopy(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
|
||||
{
|
||||
bool result = DN_Str8Builder_AddArrayCopy(builder, array, N, add);
|
||||
return result;
|
||||
bool result = DN_Str8Builder_AddArrayCopy(builder, array, N, add);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_FSTR8)
|
||||
// NOTE: [$FSTR] DN_FStr8 /////////////////////////////////////////////////////////////////////////
|
||||
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitF(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
|
||||
template <DN_USize N>
|
||||
DN_FStr8<N> DN_FStr8_InitF(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_FStr8<N> result = {};
|
||||
if (fmt) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_FStr8_AddFV(&result, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitFV(char const *fmt, va_list args)
|
||||
{
|
||||
DN_FStr8<N> result = {};
|
||||
DN_FStr8_AddFV(&result, fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> DN_USize DN_FStr8_Max(DN_FStr8<N> const *)
|
||||
{
|
||||
DN_USize result = N;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> void DN_FStr8_Clear(DN_FStr8<N> *string)
|
||||
{
|
||||
*string = {};
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_AddFV(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
|
||||
DN_USize require = DN_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/;
|
||||
DN_USize space = (N + 1) - string->size;
|
||||
result = require <= space;
|
||||
string->size += DN_VSNPRINTF(string->data + string->size, DN_CAST(int)space, fmt, args);
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not.
|
||||
string->size = DN_MIN(string->size, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_AddF(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
DN_FStr8<N> result = {};
|
||||
if (fmt) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
result = DN_FStr8_AddFV(string, fmt, args);
|
||||
DN_FStr8_AddFV(&result, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_AddCStr8(DN_FStr8<N> *string, char const *src, DN_USize size)
|
||||
template <DN_USize N>
|
||||
DN_FStr8<N> DN_FStr8_InitFV(char const *fmt, va_list args)
|
||||
{
|
||||
DN_ASSERT(string->size <= N);
|
||||
bool result = false;
|
||||
if (!string || !src || size == 0 || string->size >= N)
|
||||
return result;
|
||||
|
||||
DN_USize space = N - string->size;
|
||||
result = size <= space;
|
||||
DN_MEMCPY(string->data + string->size, src, DN_MIN(space, size));
|
||||
string->size = DN_MIN(string->size + size, N);
|
||||
string->data[string->size] = 0;
|
||||
return result;
|
||||
DN_FStr8<N> result = {};
|
||||
DN_FStr8_AddFV(&result, fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_Add(DN_FStr8<N> *string, DN_Str8 src)
|
||||
template <DN_USize N>
|
||||
DN_USize DN_FStr8_Max(DN_FStr8<N> const *)
|
||||
{
|
||||
bool result = DN_FStr8_AddCStr8(string, src.data, src.size);
|
||||
return result;
|
||||
DN_USize result = N;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> DN_Str8 DN_FStr8_ToStr8(DN_FStr8<N> const *string)
|
||||
template <DN_USize N>
|
||||
void DN_FStr8_Clear(DN_FStr8<N> *string)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!string || string->size <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DN_CAST(char *)string->data;
|
||||
result.size = string->size;
|
||||
return result;
|
||||
*string = {};
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_Eq(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs, DN_Str8EqCase eq_case)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_AddFV(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
|
||||
DN_USize require = DN_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/;
|
||||
DN_USize space = (N + 1) - string->size;
|
||||
result = require <= space;
|
||||
string->size += DN_VSNPrintF(string->data + string->size, DN_CAST(int) space, fmt, args);
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not.
|
||||
string->size = DN_Min(string->size, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_EqStr8(DN_FStr8<N> const *lhs, DN_Str8 rhs, DN_Str8EqCase eq_case)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_AddF(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs, eq_case);
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
result = DN_FStr8_AddFV(string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_EqInsensitive(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_AddCStr8(DN_FStr8<N> *string, char const *src, DN_USize size)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
|
||||
DN_Assert(string->size <= N);
|
||||
bool result = false;
|
||||
if (!string || !src || size == 0 || string->size >= N)
|
||||
return result;
|
||||
|
||||
DN_USize space = N - string->size;
|
||||
result = size <= space;
|
||||
DN_Memcpy(string->data + string->size, src, DN_Min(space, size));
|
||||
string->size = DN_Min(string->size + size, N);
|
||||
string->data[string->size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_FStr8_EqStr8Insensitive(DN_FStr8<N> const *lhs, DN_Str8 rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_Add(DN_FStr8<N> *string, DN_Str8 src)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
bool result = DN_FStr8_AddCStr8(string, src.data, src.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case)
|
||||
template <DN_USize N>
|
||||
DN_Str8 DN_FStr8_ToStr8(DN_FStr8<N> const *string)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
DN_Str8 result = {};
|
||||
if (!string || string->size <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DN_CAST(char *) string->data;
|
||||
result.size = string->size;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8Insensitive(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_Eq(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs, DN_Str8EqCase eq_case)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool operator==(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_EqStr8(DN_FStr8<N> const *lhs, DN_Str8 rhs, DN_Str8EqCase eq_case)
|
||||
{
|
||||
bool result = DN_FStr8_Eq(&lhs, &rhs, DN_Str8EqCase_Sensitive);
|
||||
return result;
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool operator!=(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_EqInsensitive(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool operator==(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
|
||||
template <DN_USize N>
|
||||
bool DN_FStr8_EqStr8Insensitive(DN_FStr8<N> const *lhs, DN_Str8 rhs)
|
||||
{
|
||||
bool result = DN_Str8_Eq(DN_FStr8_ToStr8(&lhs), rhs, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N> bool operator!=(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
|
||||
template <DN_USize A, DN_USize B>
|
||||
bool DN_FStr8_EqFStr8(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize A, DN_USize B>
|
||||
bool DN_FStr8_EqFStr8Insensitive(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs)
|
||||
{
|
||||
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
|
||||
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
|
||||
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
bool operator==(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
|
||||
{
|
||||
bool result = DN_FStr8_Eq(&lhs, &rhs, DN_Str8EqCase_Sensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
bool operator!=(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
bool operator==(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
|
||||
{
|
||||
bool result = DN_Str8_Eq(DN_FStr8_ToStr8(&lhs), rhs, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
bool operator!=(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_NO_FSTR8)
|
||||
#endif // !defined(DN_BASE_STRING_H)
|
91
Core/dn_core.cpp
Normal file
91
Core/dn_core.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
static DN_Core *g_dn_core;
|
||||
|
||||
DN_API void DN_Core_Init(DN_Core *core, DN_CoreOnInit on_init)
|
||||
{
|
||||
DN_Assert(g_dn_os_core_);
|
||||
g_dn_core = core;
|
||||
|
||||
// NOTE Initialise fields //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
core->profiler = &core->profiler_default_instance;
|
||||
#endif
|
||||
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
// NOTE: Setup the allocation table with allocation tracking turned off on
|
||||
// the arena we're using to initialise the table.
|
||||
core->alloc_table_arena = DN_Arena_InitFromOSVMem(DN_Megabytes(1), DN_Kilobytes(512), DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak);
|
||||
core->alloc_table = DN_DSMap_Init<DN_DebugAlloc>(&core->alloc_table_arena, 4096, DN_DSMapFlags_Nil);
|
||||
#endif
|
||||
|
||||
// NOTE: Print out init features ///////////////////////////////////////////////////////////////
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(tmem.arena);
|
||||
if (on_init & DN_CoreOnInit_LogLibFeatures) {
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("DN initialised:\n"));
|
||||
|
||||
DN_F64 page_size_kib = g_dn_os_core_->page_size / 1024.0;
|
||||
DN_F64 alloc_granularity_kib = g_dn_os_core_->alloc_granularity / 1024.0;
|
||||
DN_Str8Builder_AppendF(
|
||||
&builder, " OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n", page_size_kib, alloc_granularity_kib);
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (DN_ASAN_POISON) {
|
||||
DN_Str8Builder_AppendF(
|
||||
&builder, " ASAN manual poisoning%s\n", DN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
|
||||
DN_Str8Builder_AppendF(&builder, " ASAN poison guard size: %u\n", DN_ASAN_POISON_GUARD_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8(" Allocation leak tracing\n"));
|
||||
#endif
|
||||
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8(" TSC profiler available\n"));
|
||||
#endif
|
||||
// TODO(doyle): Add stacktrace feature log
|
||||
}
|
||||
|
||||
if (on_init & DN_CoreOnInit_LogCPUFeatures) {
|
||||
DN_CPUReport const *report = &g_dn_os_core_->cpu_report;
|
||||
DN_Str8 brand = DN_Str8_TrimWhitespaceAround(DN_Str8_Init(report->brand, sizeof(report->brand) - 1));
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_Str8Builder_AppendF' Actual type: 'struct DN_Str8'.
|
||||
DN_Str8Builder_AppendF(&builder, " CPU '%S' from '%s' detected:\n", brand, report->vendor);
|
||||
DN_MSVC_WARNING_POP
|
||||
|
||||
DN_USize longest_feature_name = 0;
|
||||
DN_ForIndexU(feature_index, DN_CPUFeature_Count) {
|
||||
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
|
||||
longest_feature_name = DN_Max(longest_feature_name, feature_decl.label.size);
|
||||
}
|
||||
|
||||
DN_ForIndexU(feature_index, DN_CPUFeature_Count) {
|
||||
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
|
||||
bool has_feature = DN_CPU_HasFeature(report, feature_decl.value);
|
||||
DN_Str8Builder_AppendF(&builder,
|
||||
" %.*s:%*s%s\n",
|
||||
DN_STR_FMT(feature_decl.label),
|
||||
DN_CAST(int)(longest_feature_name - feature_decl.label.size),
|
||||
"",
|
||||
has_feature ? "available" : "not available");
|
||||
}
|
||||
}
|
||||
|
||||
DN_Str8 info_log = DN_Str8Builder_Build(&builder, tmem.arena);
|
||||
if (DN_Str8_HasData(info_log))
|
||||
DN_LOG_DebugF("%.*s", DN_STR_FMT(info_log));
|
||||
}
|
||||
|
||||
DN_API void DN_Core_BeginFrame()
|
||||
{
|
||||
DN_Atomic_SetValue64(&g_dn_os_core_->mem_allocs_frame, 0);
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_API void DN_Core_SetProfiler(DN_Profiler *profiler)
|
||||
{
|
||||
if (profiler)
|
||||
g_dn_core->profiler = profiler;
|
||||
}
|
||||
#endif
|
37
Core/dn_core.h
Normal file
37
Core/dn_core.h
Normal file
@ -0,0 +1,37 @@
|
||||
#if !defined(DN_CORE_H)
|
||||
#define DN_CORE_H
|
||||
|
||||
// NOTE: DN_Core ///////////////////////////////////////////////////////////////////////////////////
|
||||
// Book-keeping data for the library and allow customisation of certain features
|
||||
// provided.
|
||||
struct DN_Core
|
||||
{
|
||||
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_DSMap<DN_DebugAlloc> alloc_table;
|
||||
DN_TicketMutex alloc_table_mutex;
|
||||
DN_Arena alloc_table_arena;
|
||||
#endif
|
||||
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
|
||||
|
||||
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_Profiler * profiler;
|
||||
DN_Profiler profiler_default_instance;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DN_CoreOnInit
|
||||
{
|
||||
DN_CoreOnInit_Nil = 0,
|
||||
DN_CoreOnInit_LogLibFeatures = 1 << 0,
|
||||
DN_CoreOnInit_LogCPUFeatures = 1 << 1,
|
||||
DN_CoreOnInit_LogAllFeatures = DN_CoreOnInit_LogLibFeatures | DN_CoreOnInit_LogCPUFeatures,
|
||||
};
|
||||
|
||||
DN_API void DN_Core_Init (DN_Core *core, DN_CoreOnInit on_init);
|
||||
DN_API void DN_Core_BeginFrame ();
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_API void DN_Core_SetProfiler (DN_Profiler *profiler);
|
||||
#endif
|
||||
#endif // !defined(DN_CORE_H)
|
442
Core/dn_core_debug.cpp
Normal file
442
Core/dn_core_debug.cpp
Normal file
@ -0,0 +1,442 @@
|
||||
#define DN_CORE_DEBUG_CPP
|
||||
|
||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk(DN_Arena *arena, uint16_t limit)
|
||||
{
|
||||
DN_StackTraceWalkResult result = {};
|
||||
#if defined(DN_OS_WIN32)
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
static DN_TicketMutex mutex = {};
|
||||
DN_TicketMutex_Begin(&mutex);
|
||||
|
||||
HANDLE thread = GetCurrentThread();
|
||||
result.process = GetCurrentProcess();
|
||||
|
||||
if (!g_dn_os_core_->win32_sym_initialised) {
|
||||
g_dn_os_core_->win32_sym_initialised = true;
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_WinError error = DN_Win_LastError(tmem.arena);
|
||||
DN_LOG_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_STR_FMT(error.msg));
|
||||
}
|
||||
}
|
||||
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 frame = {};
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
DN_FArray<uint64_t, 256> raw_frames = {};
|
||||
while (raw_frames.size < limit) {
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
|
||||
result.process,
|
||||
thread,
|
||||
&frame,
|
||||
&context,
|
||||
nullptr /*ReadMemoryRoutine*/,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
nullptr /*TranslateAddress*/))
|
||||
break;
|
||||
|
||||
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
|
||||
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
|
||||
DN_FArray_Add(&raw_frames, frame.AddrPC.Offset);
|
||||
}
|
||||
DN_TicketMutex_End(&mutex);
|
||||
|
||||
result.base_addr = DN_Arena_NewArray(arena, uint64_t, raw_frames.size, DN_ZeroMem_No);
|
||||
result.size = DN_CAST(uint16_t) raw_frames.size;
|
||||
DN_Memcpy(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
|
||||
#else
|
||||
(void)limit;
|
||||
(void)arena;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_StackTrace_AddWalkToStr8Builder_(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip)
|
||||
{
|
||||
DN_StackTraceRawFrame raw_frame = {};
|
||||
raw_frame.process = walk->process;
|
||||
for (DN_USize index = skip; index < walk->size; index++) {
|
||||
raw_frame.base_addr = walk->base_addr[index];
|
||||
DN_StackTraceFrame frame = DN_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
|
||||
DN_Str8Builder_AppendF(builder, "%.*s(%zu): %.*s%s", DN_STR_FMT(frame.file_name), frame.line_number, DN_STR_FMT(frame.function_name), (DN_CAST(int) index == walk->size - 1) ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk)
|
||||
{
|
||||
bool result = false;
|
||||
if (!it || !walk || !walk->base_addr || !walk->process)
|
||||
return result;
|
||||
|
||||
if (it->index >= walk->size)
|
||||
return false;
|
||||
|
||||
result = true;
|
||||
it->raw_frame.process = walk->process;
|
||||
it->raw_frame.base_addr = walk->base_addr[it->index++];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, uint16_t skip)
|
||||
{
|
||||
DN_Str8 result{};
|
||||
if (!walk || !arena)
|
||||
return result;
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(tmem.arena);
|
||||
DN_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8(DN_Arena *arena, uint16_t limit, uint16_t skip)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
||||
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(tmem.arena, limit);
|
||||
DN_Str8 result = DN_StackTrace_WalkResultToStr8(arena, &walk, skip);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
// NOTE: We don't use WalkResultToStr8 because that uses the TLS arenas which
|
||||
// does not use the OS heap.
|
||||
DN_Arena arena = DN_Arena_InitFromOSHeap(DN_Kilobytes(64), DN_ArenaFlags_NoAllocTrack);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(&arena);
|
||||
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(&arena, limit);
|
||||
DN_StackTrace_AddWalkToStr8Builder_(&walk, &builder, skip);
|
||||
DN_Str8 result = DN_Str8Builder_BuildFromOSHeap(&builder);
|
||||
DN_Arena_Deinit(&arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames(DN_Arena *arena, uint16_t limit)
|
||||
{
|
||||
DN_Slice<DN_StackTraceFrame> result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(tmem.arena, limit);
|
||||
if (!walk.size)
|
||||
return result;
|
||||
|
||||
DN_USize slice_index = 0;
|
||||
result = DN_Slice_Alloc<DN_StackTraceFrame>(arena, walk.size, DN_ZeroMem_No);
|
||||
for (DN_StackTraceWalkResultIterator it = {}; DN_StackTrace_WalkResultIterate(&it, &walk); ) {
|
||||
result.data[slice_index++] = DN_StackTrace_RawFrameToFrame(arena, it.raw_frame);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame(DN_Arena *arena, DN_StackTraceRawFrame raw_frame)
|
||||
{
|
||||
#if defined(DN_OS_WIN32)
|
||||
// NOTE: Get line+filename /////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: Why does zero-initialising this with `line = {};` cause
|
||||
// SymGetLineFromAddr64 function to fail once we are at
|
||||
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
|
||||
// line and file number are still valid in the result which we use, so,
|
||||
// we silently ignore this error.
|
||||
IMAGEHLP_LINEW64 line;
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
DWORD line_displacement = 0;
|
||||
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line)) {
|
||||
line = {};
|
||||
}
|
||||
|
||||
// NOTE: Get function name /////////////////////////////////////////////////////////////////////
|
||||
|
||||
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
|
||||
SYMBOL_INFOW *symbol = DN_CAST(SYMBOL_INFOW *)buffer;
|
||||
symbol->SizeOfStruct = sizeof(*symbol);
|
||||
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
|
||||
|
||||
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
|
||||
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
|
||||
|
||||
// NOTE: Construct result //////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_Str16 file_name16 = DN_Str16{line.FileName, DN_CStr16_Size(line.FileName)};
|
||||
DN_Str16 function_name16 = DN_Str16{symbol->Name, symbol->NameLen};
|
||||
|
||||
DN_StackTraceFrame result = {};
|
||||
result.address = raw_frame.base_addr;
|
||||
result.line_number = line.LineNumber;
|
||||
result.file_name = DN_Win_Str16ToStr8(arena, file_name16);
|
||||
result.function_name = DN_Win_Str16ToStr8(arena, function_name16);
|
||||
|
||||
if (!DN_Str8_HasData(result.function_name))
|
||||
result.function_name = DN_STR8("<unknown function>");
|
||||
if (!DN_Str8_HasData(result.file_name))
|
||||
result.file_name = DN_STR8("<unknown file>");
|
||||
#else
|
||||
DN_StackTraceFrame result = {};
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_StackTrace_Print(uint16_t limit)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Slice<DN_StackTraceFrame> stack_trace = DN_StackTrace_GetFrames(tmem.arena, limit);
|
||||
for (DN_StackTraceFrame &frame : stack_trace)
|
||||
DN_OS_PrintErrLnF("%.*s(%I64u): %.*s", DN_STR_FMT(frame.file_name), frame.line_number, DN_STR_FMT(frame.function_name));
|
||||
}
|
||||
|
||||
DN_API void DN_StackTrace_ReloadSymbols()
|
||||
{
|
||||
#if defined(DN_OS_WIN32)
|
||||
HANDLE process = GetCurrentProcess();
|
||||
SymRefreshModuleList(process);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: DN_Debug //////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_API void DN_Debug_TrackAlloc(void *ptr, DN_USize size, bool leak_permitted)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
|
||||
DN_DEFER
|
||||
{
|
||||
DN_TicketMutex_End(&g_dn_core->alloc_table_mutex);
|
||||
};
|
||||
|
||||
// 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.
|
||||
DN_Str8 stack_trace = DN_StackTrace_WalkStr8FromHeap(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
|
||||
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_MakeKeyU64(alloc_table, DN_CAST(uint64_t) ptr);
|
||||
DN_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc_entry.found) {
|
||||
if ((alloc->flags & DN_DebugAllocFlag_Freed) == 0) {
|
||||
DN_Str8 alloc_size = DN_CVT_U64ToByteSizeStr8(alloc_table->arena, alloc->size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_Str8 new_alloc_size = DN_CVT_U64ToByteSizeStr8(alloc_table->arena, size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_HardAssertF(
|
||||
alloc->flags & DN_DebugAllocFlag_Freed,
|
||||
"This pointer is already in the leak tracker, however it has not been freed yet. This "
|
||||
"same pointer is being ask to be tracked twice in the allocation table, e.g. one if its "
|
||||
"previous free calls has not being marked freed with an equivalent call to "
|
||||
"DN_Debug_TrackDealloc()\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is allocating %.*s again at:\n"
|
||||
"\n"
|
||||
"%.*s\n",
|
||||
ptr,
|
||||
DN_STR_FMT(alloc_size),
|
||||
DN_STR_FMT(alloc->stack_trace),
|
||||
DN_STR_FMT(new_alloc_size),
|
||||
DN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
// NOTE: Pointer was reused, clean up the prior entry
|
||||
g_dn_core->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
|
||||
g_dn_core->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size;
|
||||
|
||||
DN_OS_MemDealloc(alloc->stack_trace.data);
|
||||
DN_OS_MemDealloc(alloc->freed_stack_trace.data);
|
||||
*alloc = {};
|
||||
}
|
||||
|
||||
alloc->ptr = ptr;
|
||||
alloc->size = size;
|
||||
alloc->stack_trace = stack_trace;
|
||||
alloc->flags |= leak_permitted ? DN_DebugAllocFlag_LeakPermitted : 0;
|
||||
g_dn_core->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
|
||||
}
|
||||
|
||||
DN_API void DN_Debug_TrackDealloc(void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
|
||||
DN_DEFER { DN_TicketMutex_End(&g_dn_core->alloc_table_mutex); };
|
||||
|
||||
DN_Str8 stack_trace = DN_StackTrace_WalkStr8FromHeap(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
|
||||
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_FindKeyU64(alloc_table, DN_CAST(uintptr_t) ptr);
|
||||
DN_HardAssertF(alloc_entry.found,
|
||||
"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);
|
||||
|
||||
DN_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc->flags & DN_DebugAllocFlag_Freed) {
|
||||
DN_Str8 freed_size = DN_CVT_U64ToByteSizeStr8(alloc_table->arena, alloc->freed_size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_HardAssertF((alloc->flags & DN_DebugAllocFlag_Freed) == 0,
|
||||
"Double free detected, pointer to free was already marked "
|
||||
"as freed. Either the pointer was reallocated but not "
|
||||
"traced, or, the pointer was freed twice.\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer was freed at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is being freed again at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
,
|
||||
ptr, DN_STR_FMT(freed_size),
|
||||
DN_STR_FMT(alloc->stack_trace),
|
||||
DN_STR_FMT(alloc->freed_stack_trace),
|
||||
DN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
DN_Assert(!DN_Str8_HasData(alloc->freed_stack_trace));
|
||||
alloc->flags |= DN_DebugAllocFlag_Freed;
|
||||
alloc->freed_stack_trace = stack_trace;
|
||||
g_dn_core->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
|
||||
}
|
||||
|
||||
DN_API void DN_Debug_DumpLeaks()
|
||||
{
|
||||
uint64_t leak_count = 0;
|
||||
uint64_t leaked_bytes = 0;
|
||||
for (DN_USize index = 1; index < g_dn_core->alloc_table.occupied; index++) {
|
||||
DN_DSMapSlot<DN_DebugAlloc> *slot = g_dn_core->alloc_table.slots + index;
|
||||
DN_DebugAlloc *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & DN_DebugAllocFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & DN_DebugAllocFlag_LeakPermitted);
|
||||
if (alloc_leaked && !leak_permitted) {
|
||||
leaked_bytes += alloc->size;
|
||||
leak_count++;
|
||||
DN_Str8 alloc_size = DN_CVT_U64ToByteSizeStr8(g_dn_core->alloc_table.arena, alloc->size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_LOG_WarningF("Pointer (0x%p) leaked %.*s at:\n"
|
||||
"%.*s",
|
||||
alloc->ptr, DN_STR_FMT(alloc_size),
|
||||
DN_STR_FMT(alloc->stack_trace));
|
||||
}
|
||||
}
|
||||
|
||||
if (leak_count) {
|
||||
char buffer[512];
|
||||
DN_Arena arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_Nil);
|
||||
DN_Str8 leak_size = DN_CVT_U64ToByteSizeStr8(&arena, leaked_bytes, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_LOG_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_STR_FMT(leak_size));
|
||||
}
|
||||
}
|
||||
#endif // DN_LEAK_TRACKING
|
||||
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
// NOTE: DN_Profiler ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_ProfilerZoneScope::DN_ProfilerZoneScope(DN_Str8 name, uint16_t anchor_index)
|
||||
{
|
||||
zone = DN_Profiler_BeginZoneAtIndex(name, anchor_index);
|
||||
}
|
||||
|
||||
DN_API DN_ProfilerZoneScope::~DN_ProfilerZoneScope()
|
||||
{
|
||||
DN_Profiler_EndZone(zone);
|
||||
}
|
||||
|
||||
DN_API DN_ProfilerAnchor *DN_Profiler_ReadBuffer()
|
||||
{
|
||||
uint8_t mask = DN_ArrayCountU(g_dn_core->profiler->anchors) - 1;
|
||||
DN_ProfilerAnchor *result = g_dn_core->profiler->anchors[(g_dn_core->profiler->active_anchor_buffer - 1) & mask];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ProfilerAnchor *DN_Profiler_WriteBuffer()
|
||||
{
|
||||
uint8_t mask = DN_ArrayCountU(g_dn_core->profiler->anchors) - 1;
|
||||
DN_ProfilerAnchor *result = g_dn_core->profiler->anchors[(g_dn_core->profiler->active_anchor_buffer + 0) & mask];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ProfilerZone DN_Profiler_BeginZoneAtIndex(DN_Str8 name, uint16_t anchor_index)
|
||||
{
|
||||
DN_ProfilerAnchor *anchor = DN_Profiler_WriteBuffer() + anchor_index;
|
||||
// TODO: We need per-thread-local-storage profiler so that we can use these apis
|
||||
// across threads. For now, we let them overwrite each other but this is not tenable.
|
||||
#if 0
|
||||
if (DN_Str8_HasData(anchor->name) && anchor->name != name)
|
||||
DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_STR_FMT(anchor->name), DN_STR_FMT(name));
|
||||
#endif
|
||||
anchor->name = name;
|
||||
DN_ProfilerZone result = {};
|
||||
result.begin_tsc = DN_CPU_TSC();
|
||||
result.anchor_index = anchor_index;
|
||||
result.parent_zone = g_dn_core->profiler->parent_zone;
|
||||
result.elapsed_tsc_at_zone_start = anchor->tsc_inclusive;
|
||||
g_dn_core->profiler->parent_zone = anchor_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Profiler_EndZone(DN_ProfilerZone zone)
|
||||
{
|
||||
uint64_t elapsed_tsc = DN_CPU_TSC() - zone.begin_tsc;
|
||||
DN_ProfilerAnchor *anchor_buffer = DN_Profiler_WriteBuffer();
|
||||
DN_ProfilerAnchor *anchor = anchor_buffer + zone.anchor_index;
|
||||
|
||||
anchor->hit_count++;
|
||||
anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc;
|
||||
anchor->tsc_exclusive += elapsed_tsc;
|
||||
|
||||
DN_ProfilerAnchor *parent_anchor = anchor_buffer + zone.parent_zone;
|
||||
parent_anchor->tsc_exclusive -= elapsed_tsc;
|
||||
g_dn_core->profiler->parent_zone = zone.parent_zone;
|
||||
}
|
||||
|
||||
DN_API void DN_Profiler_SwapAnchorBuffer()
|
||||
{
|
||||
g_dn_core->profiler->active_anchor_buffer++;
|
||||
g_dn_core->profiler->parent_zone = 0;
|
||||
DN_ProfilerAnchor *anchors = DN_Profiler_WriteBuffer();
|
||||
DN_Memset(anchors,
|
||||
0,
|
||||
DN_ArrayCountU(g_dn_core->profiler->anchors[0]) * sizeof(g_dn_core->profiler->anchors[0][0]));
|
||||
}
|
||||
|
||||
DN_API void DN_Profiler_Dump(uint64_t tsc_per_second)
|
||||
{
|
||||
DN_ProfilerAnchor *anchors = DN_Profiler_ReadBuffer();
|
||||
for (size_t anchor_index = 1; anchor_index < DN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) {
|
||||
DN_ProfilerAnchor const *anchor = anchors + anchor_index;
|
||||
if (!anchor->hit_count)
|
||||
continue;
|
||||
|
||||
uint64_t tsc_exclusive = anchor->tsc_exclusive;
|
||||
uint64_t tsc_inclusive = anchor->tsc_inclusive;
|
||||
DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_CAST(DN_F64) tsc_per_second;
|
||||
if (tsc_exclusive == tsc_inclusive) {
|
||||
DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_STR_FMT(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
|
||||
} else {
|
||||
DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_CAST(DN_F64) tsc_per_second;
|
||||
DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms",
|
||||
DN_STR_FMT(anchor->name),
|
||||
anchor->hit_count,
|
||||
tsc_exclusive_milliseconds,
|
||||
tsc_inclusive_milliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !defined(DN_NO_PROFILER)
|
||||
|
98
Core/dn_core_debug.h
Normal file
98
Core/dn_core_debug.h
Normal file
@ -0,0 +1,98 @@
|
||||
#if !defined(DN_CORE_DEBUG_H)
|
||||
#define DN_CORE_DEBUG_H
|
||||
|
||||
// NOTE: DN_StackTrace /////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Debug //////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_DebugAllocFlag
|
||||
{
|
||||
DN_DebugAllocFlag_Freed = 1 << 0,
|
||||
DN_DebugAllocFlag_LeakPermitted = 1 << 1,
|
||||
};
|
||||
|
||||
struct DN_DebugAlloc
|
||||
{
|
||||
void *ptr; // 8 Pointer to the allocation being tracked
|
||||
DN_USize size; // 16 Size of the allocation
|
||||
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
|
||||
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||
DN_U16 flags; // 72 Bit flags from `DN_DebugAllocFlag`
|
||||
};
|
||||
|
||||
static_assert(sizeof(DN_DebugAlloc) == 64 || sizeof(DN_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
|
||||
"We aim to keep the allocation record as light as possible as "
|
||||
"memory tracking can get expensive. Enforce that there is no "
|
||||
"unexpected padding.");
|
||||
|
||||
// NOTE: DN_Profiler ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
#if !defined(DN_PROFILER_ANCHOR_BUFFER_SIZE)
|
||||
#define DN_PROFILER_ANCHOR_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
struct DN_ProfilerAnchor
|
||||
{
|
||||
// Inclusive refers to the time spent to complete the function call
|
||||
// including all children functions.
|
||||
//
|
||||
// Exclusive refers to the time spent in the function, not including any
|
||||
// time spent in children functions that we call that are also being
|
||||
// profiled. If we recursively call into ourselves, the time we spent in
|
||||
// our function is accumulated.
|
||||
DN_U64 tsc_inclusive;
|
||||
DN_U64 tsc_exclusive;
|
||||
DN_U16 hit_count;
|
||||
DN_Str8 name;
|
||||
};
|
||||
|
||||
struct DN_ProfilerZone
|
||||
{
|
||||
DN_U16 anchor_index;
|
||||
DN_U64 begin_tsc;
|
||||
DN_U16 parent_zone;
|
||||
DN_U64 elapsed_tsc_at_zone_start;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
struct DN_ProfilerZoneScope
|
||||
{
|
||||
DN_ProfilerZoneScope(DN_Str8 name, DN_U16 anchor_index);
|
||||
~DN_ProfilerZoneScope();
|
||||
DN_ProfilerZone zone;
|
||||
};
|
||||
|
||||
#define DN_Profiler_ZoneScopeAtIndex(name, anchor_index) auto DN_UniqueName(profile_zone_) = DN_ProfilerZoneScope(DN_STR8(name), anchor_index)
|
||||
#define DN_Profiler_ZoneScope(name) DN_Profiler_ZoneScopeAtIndex(name, __COUNTER__ + 1)
|
||||
#endif
|
||||
|
||||
#define DN_Profiler_ZoneBlockIndex(name, index) \
|
||||
for (DN_ProfilerZone DN_UniqueName(profile_zone__) = DN_Profiler_BeginZoneAtIndex(name, index), DN_UniqueName(dummy__) = {}; \
|
||||
DN_UniqueName(dummy__).begin_tsc == 0; \
|
||||
DN_Profiler_EndZone(DN_UniqueName(profile_zone__)), DN_UniqueName(dummy__).begin_tsc = 1)
|
||||
|
||||
#define DN_Profiler_ZoneBlock(name) DN_Profiler_ZoneBlockIndex(DN_STR8(name), __COUNTER__ + 1)
|
||||
|
||||
enum DN_ProfilerAnchorBuffer
|
||||
{
|
||||
DN_ProfilerAnchorBuffer_Back,
|
||||
DN_ProfilerAnchorBuffer_Front,
|
||||
};
|
||||
|
||||
struct DN_Profiler
|
||||
{
|
||||
DN_ProfilerAnchor anchors[2][DN_PROFILER_ANCHOR_BUFFER_SIZE];
|
||||
DN_U8 active_anchor_buffer;
|
||||
DN_U16 parent_zone;
|
||||
};
|
||||
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_ReadBuffer ();
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_WriteBuffer ();
|
||||
#define DN_Profiler_BeginZone(name) DN_Profiler_BeginZoneAtIndex(DN_STR8(name), __COUNTER__ + 1)
|
||||
DN_API DN_ProfilerZone DN_Profiler_BeginZoneAtIndex (DN_Str8 name, DN_U16 anchor_index);
|
||||
DN_API void DN_Profiler_EndZone (DN_ProfilerZone zone);
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_AnchorBuffer (DN_ProfilerAnchorBuffer buffer);
|
||||
DN_API void DN_Profiler_SwapAnchorBuffer ();
|
||||
DN_API void DN_Profiler_Dump (DN_U64 tsc_per_second);
|
||||
|
||||
#endif // !defined(DN_NO_PROFILER)
|
||||
#endif // DN_CORE_DEBUG_H
|
1218
Core/dn_core_demo.cpp
Normal file
1218
Core/dn_core_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
260
dqn_external.cpp → External/stb_sprintf.h
vendored
260
dqn_external.cpp → External/stb_sprintf.h
vendored
@ -1,23 +1,229 @@
|
||||
// NOTE: Additional *unofficial* changes
|
||||
// - Adding STBSP__ASAN to STBSP__PUBLICDEC so that MSVC's ASAN does not trigger (https://github.com/nothings/stb/pull/1350)
|
||||
// - Adding __attribute__((no_sanitize("undefined"))) to STBSP__ASAN for CLANG so that UBSAN does not trigger (https://github.com/nothings/stb/pull/1477)
|
||||
// - Adding '%S' for DN_Str8
|
||||
|
||||
// stb_sprintf - v1.10 - public domain snprintf() implementation
|
||||
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
|
||||
// http://github.com/nothings/stb
|
||||
//
|
||||
// allowed types: sc uidBboXx p AaGgEef n
|
||||
// lengths : hh h ll j z t I64 I32 I
|
||||
//
|
||||
// Contributors:
|
||||
// Fabian "ryg" Giesen (reformatting)
|
||||
// github:aganm (attribute format)
|
||||
//
|
||||
// Contributors (bugfixes):
|
||||
// github:d26435
|
||||
// github:trex78
|
||||
// github:account-login
|
||||
// Jari Komppa (SI suffixes)
|
||||
// Rohit Nirmal
|
||||
// Marcin Wojdyr
|
||||
// Leonard Ritter
|
||||
// Stefano Zanotti
|
||||
// Adam Allison
|
||||
// Arvid Gerstmann
|
||||
// Markus Kolb
|
||||
//
|
||||
// LICENSE:
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_SPRINTF_H_INCLUDE
|
||||
#define STB_SPRINTF_H_INCLUDE
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
// $$ _____|$$ | $$ |\__$$ __|$$ _____|$$ __$$\ $$$\ $$ |$$ __$$\ $$ |
|
||||
// $$ | \$$\ $$ | $$ | $$ | $$ | $$ |$$$$\ $$ |$$ / $$ |$$ |
|
||||
// $$$$$\ \$$$$ / $$ | $$$$$\ $$$$$$$ |$$ $$\$$ |$$$$$$$$ |$$ |
|
||||
// $$ __| $$ $$< $$ | $$ __| $$ __$$< $$ \$$$$ |$$ __$$ |$$ |
|
||||
// $$ | $$ /\$$\ $$ | $$ | $$ | $$ |$$ |\$$$ |$$ | $$ |$$ |
|
||||
// $$$$$$$$\ $$ / $$ | $$ | $$$$$$$$\ $$ | $$ |$$ | \$$ |$$ | $$ |$$$$$$$$\
|
||||
// \________|\__| \__| \__| \________|\__| \__|\__| \__|\__| \__|\________|
|
||||
//
|
||||
// dqn_external.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Single file sprintf replacement.
|
||||
|
||||
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
|
||||
Hereby placed in public domain.
|
||||
|
||||
This is a full sprintf replacement that supports everything that
|
||||
the C runtime sprintfs support, including float/double, 64-bit integers,
|
||||
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
|
||||
|
||||
Why would you need this if sprintf already exists? Well, first off,
|
||||
it's *much* faster (see below). It's also much smaller than the CRT
|
||||
versions code-space-wise. We've also added some simple improvements
|
||||
that are super handy (commas in thousands, callbacks at buffer full,
|
||||
for example). Finally, the format strings for MSVC and GCC differ
|
||||
for 64-bit integers (among other small things), so this lets you use
|
||||
the same format strings in cross platform code.
|
||||
|
||||
It uses the standard single file trick of being both the header file
|
||||
and the source itself. If you just include it normally, you just get
|
||||
the header file function definitions. To get the code, you include
|
||||
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
|
||||
|
||||
It only uses va_args macros from the C runtime to do it's work. It
|
||||
does cast doubles to S64s and shifts and divides U64s, which does
|
||||
drag in CRT code on most platforms.
|
||||
|
||||
It compiles to roughly 8K with float support, and 4K without.
|
||||
As a comparison, when using MSVC static libs, calling sprintf drags
|
||||
in 16K.
|
||||
|
||||
API:
|
||||
====
|
||||
int stbsp_sprintf( char * buf, char const * fmt, ... )
|
||||
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
|
||||
Convert an arg list into a buffer. stbsp_snprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
|
||||
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
|
||||
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
|
||||
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
|
||||
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
|
||||
Your callback can then copy the chars out, print them or whatever.
|
||||
This function is actually the workhorse for everything else.
|
||||
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
|
||||
// you return the next buffer to use or 0 to stop converting
|
||||
|
||||
void stbsp_set_separators( char comma, char period )
|
||||
Set the comma and period characters to use.
|
||||
|
||||
FLOATS/DOUBLES:
|
||||
===============
|
||||
This code uses a internal float->ascii conversion method that uses
|
||||
doubles with error correction (double-doubles, for ~105 bits of
|
||||
precision). This conversion is round-trip perfect - that is, an atof
|
||||
of the values output here will give you the bit-exact double back.
|
||||
|
||||
One difference is that our insignificant digits will be different than
|
||||
with MSVC or GCC (but they don't match each other either). We also
|
||||
don't attempt to find the minimum length matching float (pre-MSVC15
|
||||
doesn't either).
|
||||
|
||||
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
|
||||
and you'll save 4K of code space.
|
||||
|
||||
64-BIT INTS:
|
||||
============
|
||||
This library also supports 64-bit integers and you can use MSVC style or
|
||||
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
|
||||
for size_t and ptr_diff_t (%jd %zd) as well.
|
||||
|
||||
EXTRAS:
|
||||
=======
|
||||
Like some GCCs, for integers and floats, you can use a ' (single quote)
|
||||
specifier and commas will be inserted on the thousands: "%'d" on 12345
|
||||
would print 12,345.
|
||||
|
||||
For integers and floats, you can use a "$" specifier and the number
|
||||
will be converted to float and then divided to get kilo, mega, giga or
|
||||
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
|
||||
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
|
||||
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
|
||||
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
|
||||
suffix, add "_" specifier: "%_$d" -> "2.53M".
|
||||
|
||||
In addition to octal and hexadecimal conversions, you can print
|
||||
integers in binary: "%b" for 256 would print 100.
|
||||
|
||||
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
|
||||
===================================================================
|
||||
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
|
||||
"%24d" across all 32-bit ints (4.5x/4.2x faster)
|
||||
"%x" across all 32-bit ints (4.5x/3.8x faster)
|
||||
"%08x" across all 32-bit ints (4.3x/3.8x faster)
|
||||
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
|
||||
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
|
||||
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
|
||||
"%f" for values near e-300 (7.9x/6.5x faster)
|
||||
"%f" for values near e+300 (10.0x/9.1x faster)
|
||||
"%e" for values near e-300 (10.1x/7.0x faster)
|
||||
"%e" for values near e+300 (9.2x/6.0x faster)
|
||||
"%.320f" for values near e-300 (12.6x/11.2x faster)
|
||||
"%a" for random values (8.6x/4.3x faster)
|
||||
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
|
||||
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
|
||||
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
|
||||
"...512 char string..." ( 35.0x/32.5x faster!)
|
||||
*/
|
||||
|
||||
#if !defined(DN_USE_STD_PRINTF) && !defined(DN_STB_SPRINTF_HEADER_ONLY)
|
||||
// NOTE: [$STBS] stb_sprintf ///////////////////////////////////////////////////////////////////////
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && defined(__has_attribute)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#if __has_attribute(__no_sanitize__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
|
||||
#elif __has_attribute(__no_sanitize_address__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#elif __has_attribute(__no_address_safety_analysis__)
|
||||
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#if defined(__SANITIZE_ADDRESS__)
|
||||
#define STBSP__ASAN __declspec(no_sanitize_address)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ASAN
|
||||
#define STBSP__ASAN
|
||||
#endif
|
||||
|
||||
#ifdef STB_SPRINTF_STATIC
|
||||
#define STBSP__PUBLICDEC static STBSP__ASAN
|
||||
#define STBSP__PUBLICDEF static STBSP__ASAN
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#define STBSP__PUBLICDEC extern "C" STBSP__ASAN
|
||||
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
|
||||
#else
|
||||
#define STBSP__PUBLICDEC extern STBSP__ASAN
|
||||
#define STBSP__PUBLICDEF STBSP__ASAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ATTRIBUTE_FORMAT
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBSP__NOTUSED(v) (void)(v)
|
||||
#else
|
||||
#define STBSP__NOTUSED(v) (void)sizeof(v)
|
||||
#endif
|
||||
|
||||
#include <stdarg.h> // for va_arg(), va_list()
|
||||
#include <stddef.h> // size_t, ptrdiff_t
|
||||
|
||||
#ifndef STB_SPRINTF_MIN
|
||||
#define STB_SPRINTF_MIN 512 // how many characters per callback
|
||||
#endif
|
||||
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
|
||||
|
||||
#ifndef STB_SPRINTF_DECORATE
|
||||
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
|
||||
#endif
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
|
||||
|
||||
#endif // STB_SPRINTF_H_INCLUDE
|
||||
|
||||
#ifdef STB_SPRINTF_IMPLEMENTATION
|
||||
|
||||
#define stbsp__uint32 unsigned int
|
||||
@ -149,9 +355,6 @@ static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uin
|
||||
return (stbsp__uint32)(sn - s);
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
#endif
|
||||
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
|
||||
{
|
||||
static char hex[] = "0123456789abcdefxp";
|
||||
@ -406,6 +609,18 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback,
|
||||
// copy the string in
|
||||
goto scopy;
|
||||
|
||||
case 'S':
|
||||
{
|
||||
DN_Str8 str8 = va_arg(va, DN_Str8);
|
||||
s = (char *)str8.data;
|
||||
l = (uint32_t)str8.size;
|
||||
lead[0] = 0;
|
||||
tail[0] = 0;
|
||||
pr = 0;
|
||||
dp = 0;
|
||||
cs = 0;
|
||||
}goto scopy;
|
||||
|
||||
case 'c': // char
|
||||
// get the character
|
||||
s = num + STBSP__NUMSZ - 1;
|
||||
@ -1176,7 +1391,7 @@ done:
|
||||
#undef stbsp__flush_cb
|
||||
#undef stbsp__cb_buf_clamp
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////////
|
||||
// ============================================================================
|
||||
// wrapper functions
|
||||
|
||||
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
|
||||
@ -1280,7 +1495,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt,
|
||||
return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////////
|
||||
// =======================================================================
|
||||
// low level float utility functions
|
||||
|
||||
#ifndef STB_SPRINTF_NOFLOAT
|
||||
@ -1710,4 +1925,3 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#endif // !defined(DN_USE_STD_PRINTF) && !defined(DN_STB_SPRINTF_HEADER_ONLY)
|
170
Extra/dn_bin_pack.cpp
Normal file
170
Extra/dn_bin_pack.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
DN_API void DN_BinPack_U64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item)
|
||||
{
|
||||
DN_U64 const VALUE_MASK = 0b0111'1111;
|
||||
DN_U8 const CONTINUE_BIT = 0b1000'0000;
|
||||
|
||||
if (mode == DN_BinPackMode_Serialise) {
|
||||
DN_U64 it = *item;
|
||||
do {
|
||||
DN_U8 write_value = DN_CAST(DN_U8)(it & VALUE_MASK);
|
||||
it >>= 7;
|
||||
if (it)
|
||||
write_value |= CONTINUE_BIT;
|
||||
DN_Str8Builder_AppendBytesCopy(&pack->writer, &write_value, sizeof(write_value));
|
||||
} while (it);
|
||||
} else {
|
||||
*item = 0;
|
||||
DN_USize bits_read = 0;
|
||||
for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) {
|
||||
src = pack->read.data[pack->read_index++];
|
||||
DN_U8 masked_src = src & VALUE_MASK;
|
||||
*item |= (DN_CAST(DN_U64) masked_src << bits_read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_VarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size)
|
||||
{
|
||||
DN_U64 value = 0;
|
||||
DN_AssertF(size <= sizeof(value),
|
||||
"An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported",
|
||||
size * 8);
|
||||
|
||||
if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value`
|
||||
DN_Memcpy(&value, item, size);
|
||||
|
||||
DN_BinPack_U64(pack, mode, &value);
|
||||
|
||||
if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item`
|
||||
DN_Memcpy(item, &value, size);
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_U32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_U16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_U8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_I64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_I32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_I16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_I8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_F64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_F32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
#if defined(DN_MATH_H)
|
||||
DN_API void DN_BinPack_V2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item)
|
||||
{
|
||||
DN_BinPack_F32(pack, mode, &item->x);
|
||||
DN_BinPack_F32(pack, mode, &item->y);
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_V4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item)
|
||||
{
|
||||
DN_BinPack_F32(pack, mode, &item->x);
|
||||
DN_BinPack_F32(pack, mode, &item->y);
|
||||
DN_BinPack_F32(pack, mode, &item->z);
|
||||
DN_BinPack_F32(pack, mode, &item->w);
|
||||
}
|
||||
#endif
|
||||
|
||||
DN_API void DN_BinPack_Bool(DN_BinPack *pack, DN_BinPackMode mode, bool *item)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, item, sizeof(*item));
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_Str8(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, &string->size, sizeof(string->size));
|
||||
if (mode == DN_BinPackMode_Serialise) {
|
||||
DN_Str8Builder_AppendBytesCopy(&pack->writer, string->data, string->size);
|
||||
} else {
|
||||
DN_Str8 src = DN_Str8_Slice(pack->read, pack->read_index, string->size);
|
||||
*string = DN_Str8_Copy(arena, src);
|
||||
pack->read_index += src.size;
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_Str8Pool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, &string->size, sizeof(string->size));
|
||||
if (mode == DN_BinPackMode_Serialise) {
|
||||
DN_Str8Builder_AppendBytesCopy(&pack->writer, string->data, string->size);
|
||||
} else {
|
||||
DN_Str8 src = DN_Str8_Slice(pack->read, pack->read_index, string->size);
|
||||
*string = DN_Pool_AllocStr8Copy(pool, src);
|
||||
pack->read_index += src.size;
|
||||
}
|
||||
}
|
||||
|
||||
template <DN_USize N>
|
||||
DN_API void DN_BinPack_FStr8(DN_BinPack *pack, DN_BinPackMode mode, DN_FStr8<N> *string)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, &string->size, sizeof(string->size));
|
||||
if (mode == DN_BinPackMode_Serialise) {
|
||||
DN_Str8Builder_AppendBytesCopy(&pack->writer, string->data, string->size);
|
||||
} else {
|
||||
DN_Str8 src = DN_Str8_Slice(pack->read, pack->read_index, string->size);
|
||||
*string = DN_FStr8_InitF<N>("%.*s", DN_STR_FMT(src));
|
||||
pack->read_index += src.size;
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_Bytes(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size)
|
||||
{
|
||||
DN_Str8 string = DN_Str8_Init(*ptr, *size);
|
||||
DN_BinPack_Str8(pack, arena, mode, &string);
|
||||
*ptr = string.data;
|
||||
*size = string.size;
|
||||
}
|
||||
|
||||
DN_API void DN_BinPack_CArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size)
|
||||
{
|
||||
DN_BinPack_VarInt_(pack, mode, &size, sizeof(size));
|
||||
if (mode == DN_BinPackMode_Serialise) {
|
||||
DN_Str8Builder_AppendBytesCopy(&pack->writer, ptr, size);
|
||||
} else {
|
||||
DN_Str8 src = DN_Str8_Slice(pack->read, pack->read_index, size);
|
||||
DN_Assert(src.size == size);
|
||||
DN_Memcpy(ptr, src.data, DN_Min(src.size, size));
|
||||
pack->read_index += src.size;
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_BinPack_Build(DN_BinPack const *pack, DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = DN_Str8Builder_Build(&pack->writer, arena);
|
||||
return result;
|
||||
}
|
43
Extra/dn_bin_pack.h
Normal file
43
Extra/dn_bin_pack.h
Normal file
@ -0,0 +1,43 @@
|
||||
#if !defined(DN_BIN_PACK_H)
|
||||
#define DN_BIN_PACK_H
|
||||
|
||||
#if !defined(DN_BASE_INC_H)
|
||||
#error dn_base_inc.h must be included before this
|
||||
#endif
|
||||
|
||||
enum DN_BinPackMode
|
||||
{
|
||||
DN_BinPackMode_Serialise,
|
||||
DN_BinPackMode_Deserialise,
|
||||
};
|
||||
|
||||
struct DN_BinPack
|
||||
{
|
||||
DN_Str8Builder writer;
|
||||
DN_Str8 read;
|
||||
DN_USize read_index;
|
||||
};
|
||||
|
||||
DN_API void DN_BinPack_U64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item);
|
||||
DN_API void DN_BinPack_U32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item);
|
||||
DN_API void DN_BinPack_U16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item);
|
||||
DN_API void DN_BinPack_U8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item);
|
||||
DN_API void DN_BinPack_I64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item);
|
||||
DN_API void DN_BinPack_I32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item);
|
||||
DN_API void DN_BinPack_I16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item);
|
||||
DN_API void DN_BinPack_I8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item);
|
||||
DN_API void DN_BinPack_F64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item);
|
||||
DN_API void DN_BinPack_F32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item);
|
||||
#if defined(DN_MATH_H)
|
||||
DN_API void DN_BinPack_V2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item);
|
||||
DN_API void DN_BinPack_V4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item);
|
||||
#endif
|
||||
DN_API void DN_BinPack_Bool (DN_BinPack *pack, DN_BinPackMode mode, bool *item);
|
||||
DN_API void DN_BinPack_Str8 (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string);
|
||||
DN_API void DN_BinPack_Str8Pool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string);
|
||||
template <DN_USize N> DN_API void DN_BinPack_FStr8 (DN_BinPack *pack, DN_BinPackMode mode, DN_FStr8<N> *string);
|
||||
DN_API void DN_BinPack_Bytes (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size);
|
||||
DN_API void DN_BinPack_CArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size);
|
||||
DN_API DN_Str8 DN_BinPack_Build (DN_BinPack const *pack, DN_Arena *arena);
|
||||
|
||||
#endif // !defined(DN_BIN_PACK_H)
|
1288
Extra/dn_cgen.cpp
Normal file
1288
Extra/dn_cgen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
200
Extra/dn_cgen.h
Normal file
200
Extra/dn_cgen.h
Normal file
@ -0,0 +1,200 @@
|
||||
#if !defined(DN_CGEN_H)
|
||||
#define DN_CGEN_H
|
||||
|
||||
#if !defined(DN_NO_METADESK)
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define DN_UNDO_CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
// NOTE: Metadesk does not have the header for 'size_t'
|
||||
#if defined(DN_COMPILER_GCC)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#define MD_DEFAULT_SPRINTF 0
|
||||
#define MD_IMPL_Vsnprintf DN_VSNPrintF
|
||||
#include "../External/metadesk/md.h"
|
||||
#if defined(DN_UNDO_CRT_SECURE_NO_WARNINGS)
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DN_CPP_FILE_H)
|
||||
#error dn_cpp_file.h must be included before this
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_WINDOWS) && !defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER)
|
||||
#error DN <Windows.h> replacement header must be disabled with DN_NO_WINDOWS_H_REPLACEMENT_HEADER since Metadesk includes <Windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(MD_H)
|
||||
#error Metadesk 'md.h' must be included before 'dn_cgen.h'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_BASE_INC_H)
|
||||
#error dn_base_inc.h must be included before this
|
||||
#endif
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$ _____|$$$\ $$ |
|
||||
// $$ / \__|$$ / \__|$$ | $$$$\ $$ |
|
||||
// $$ | $$ |$$$$\ $$$$$\ $$ $$\$$ |
|
||||
// $$ | $$ |\_$$ |$$ __| $$ \$$$$ |
|
||||
// $$ | $$\ $$ | $$ |$$ | $$ |\$$$ |
|
||||
// \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | \$$ |
|
||||
// \______/ \______/ \________|\__| \__|
|
||||
//
|
||||
// dn_cgen.h -- C/C++ code generation from table data in Metadesk files
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
enum DN_CGenTableKeyType
|
||||
{
|
||||
DN_CGenTableKeyType_Nil,
|
||||
DN_CGenTableKeyType_Name,
|
||||
DN_CGenTableKeyType_Type,
|
||||
};
|
||||
|
||||
enum DN_CGenTableType
|
||||
{
|
||||
DN_CGenTableType_Nil,
|
||||
DN_CGenTableType_Data,
|
||||
DN_CGenTableType_CodeGenBuiltinTypes,
|
||||
DN_CGenTableType_CodeGenStruct,
|
||||
DN_CGenTableType_CodeGenEnum,
|
||||
DN_CGenTableType_Count,
|
||||
};
|
||||
|
||||
enum DN_CGenTableRowTagType
|
||||
{
|
||||
DN_CGenTableRowTagType_Nil,
|
||||
DN_CGenTableRowTagType_CommentDivider,
|
||||
DN_CGenTableRowTagType_EmptyLine,
|
||||
};
|
||||
|
||||
enum DN_CGenTableRowTagCommentDivider
|
||||
{
|
||||
DN_CGenTableRowTagCommentDivider_Nil,
|
||||
DN_CGenTableRowTagCommentDivider_Label,
|
||||
};
|
||||
|
||||
enum DN_CGenTableHeaderType
|
||||
{
|
||||
DN_CGenTableHeaderType_Name,
|
||||
DN_CGenTableHeaderType_Table,
|
||||
DN_CGenTableHeaderType_CppType,
|
||||
DN_CGenTableHeaderType_CppName,
|
||||
DN_CGenTableHeaderType_CppValue,
|
||||
DN_CGenTableHeaderType_CppIsPtr,
|
||||
DN_CGenTableHeaderType_CppOpEquals,
|
||||
DN_CGenTableHeaderType_CppArraySize,
|
||||
DN_CGenTableHeaderType_CppArraySizeField,
|
||||
DN_CGenTableHeaderType_CppLabel,
|
||||
DN_CGenTableHeaderType_GenTypeInfo,
|
||||
DN_CGenTableHeaderType_GenEnumCount,
|
||||
DN_CGenTableHeaderType_Count,
|
||||
};
|
||||
|
||||
struct DN_CGenTableHeader
|
||||
{
|
||||
MD_String8 name;
|
||||
int longest_string;
|
||||
};
|
||||
|
||||
struct DN_CGenTableRowTag
|
||||
{
|
||||
DN_CGenTableRowTagType type;
|
||||
MD_String8 comment;
|
||||
DN_CGenTableRowTag *next;
|
||||
};
|
||||
|
||||
struct DN_CGenTableColumn
|
||||
{
|
||||
MD_Node *node;
|
||||
DN_Str8 string;
|
||||
};
|
||||
|
||||
struct DN_CGenTableRow
|
||||
{
|
||||
DN_CGenTableRowTag *first_tag;
|
||||
DN_CGenTableRowTag *last_tag;
|
||||
DN_CGenTableColumn *columns;
|
||||
};
|
||||
|
||||
struct DN_CGenTable
|
||||
{
|
||||
DN_CGenTableType type;
|
||||
DN_Str8 name;
|
||||
MD_Map headers_map;
|
||||
DN_CGenTableHeader *headers;
|
||||
DN_CGenTableRow *rows;
|
||||
size_t column_count;
|
||||
size_t row_count;
|
||||
|
||||
MD_Node *node;
|
||||
MD_Node *headers_node;
|
||||
DN_USize column_indexes[DN_CGenTableHeaderType_Count];
|
||||
DN_CGenTable *next;
|
||||
};
|
||||
|
||||
struct DN_CGen
|
||||
{
|
||||
MD_Arena *arena;
|
||||
MD_Node *file_list;
|
||||
MD_Map table_map;
|
||||
DN_CGenTable *first_table;
|
||||
DN_CGenTable *last_table;
|
||||
DN_USize table_counts[DN_CGenTableType_Count];
|
||||
};
|
||||
|
||||
struct DN_CGenMapNodeToEnum
|
||||
{
|
||||
uint32_t enum_val;
|
||||
DN_Str8 node_string;
|
||||
};
|
||||
|
||||
struct DN_CGenLookupTableIterator
|
||||
{
|
||||
DN_CGenTable *cgen_table;
|
||||
DN_CGenTableRow *cgen_table_row;
|
||||
DN_CGenTableColumn cgen_table_column[DN_CGenTableHeaderType_Count];
|
||||
DN_CGenTable *table;
|
||||
DN_USize row_index;
|
||||
};
|
||||
|
||||
struct DN_CGenLookupColumnAtHeader
|
||||
{
|
||||
DN_USize index;
|
||||
DN_CGenTableHeader header;
|
||||
DN_CGenTableColumn column;
|
||||
};
|
||||
|
||||
enum DN_CGenEmit
|
||||
{
|
||||
DN_CGenEmit_Prototypes = 1 << 0,
|
||||
DN_CGenEmit_Implementation = 1 << 1,
|
||||
};
|
||||
|
||||
#define DN_CGen_MDToDNStr8(str8) DN_Str8_Init((str8).str, (str8).size)
|
||||
#define DN_CGen_DNToMDStr8(str8) \
|
||||
{ \
|
||||
DN_CAST(MD_u8 *) \
|
||||
(str8).data, \
|
||||
(str8).size \
|
||||
}
|
||||
|
||||
DN_API DN_CGen DN_CGen_InitFilesArgV(int argc, char const **argv, DN_OSErrSink *err);
|
||||
DN_API DN_Str8 DN_CGen_TableHeaderTypeToDeclStr8(DN_CGenTableHeaderType type);
|
||||
DN_API DN_CGenMapNodeToEnum DN_CGen_MapNodeToEnumOrExit(MD_Node const *node, DN_CGenMapNodeToEnum const *valid_keys, DN_USize valid_keys_size, char const *fmt, ...);
|
||||
DN_API DN_USize DN_CGen_NodeChildrenCount(MD_Node const *node);
|
||||
DN_API void DN_CGen_LogF(MD_MessageKind kind, MD_Node *node, DN_OSErrSink *err, char const *fmt, ...);
|
||||
DN_API bool DN_CGen_TableHasHeaders(DN_CGenTable const *table, DN_Str8 const *headers, DN_USize header_count, DN_OSErrSink *err);
|
||||
DN_API DN_CGenLookupColumnAtHeader DN_CGen_LookupColumnAtHeader(DN_CGenTable *table, DN_Str8 header, DN_CGenTableRow const *row);
|
||||
DN_API bool DN_CGen_LookupNextTableInCodeGenTable(DN_CGen *cgen, DN_CGenTable *cgen_table, DN_CGenLookupTableIterator *it);
|
||||
DN_API void DN_CGen_EmitCodeForTables(DN_CGen *cgen, DN_CGenEmit emit, DN_CppFile *cpp, DN_Str8 emit_prefix);
|
||||
#endif // DN_CGEN_H
|
288
Extra/dn_csv.cpp
Normal file
288
Extra/dn_csv.cpp
Normal file
@ -0,0 +1,288 @@
|
||||
#include "dn_csv.h"
|
||||
|
||||
static DN_CSVTokeniser DN_CSV_TokeniserInit(DN_Str8 string, char delimiter)
|
||||
{
|
||||
DN_CSVTokeniser result = {};
|
||||
result.string = string;
|
||||
result.delimiter = delimiter;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool DN_CSV_TokeniserValid(DN_CSVTokeniser *tokeniser)
|
||||
{
|
||||
bool result = tokeniser && !tokeniser->bad;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool DN_CSV_TokeniserNextRow(DN_CSVTokeniser *tokeniser)
|
||||
{
|
||||
bool result = false;
|
||||
if (DN_CSV_TokeniserValid(tokeniser) && DN_Str8_HasData(tokeniser->string)) {
|
||||
// NOTE: First time querying row iterator is nil, let tokeniser advance
|
||||
if (tokeniser->it) {
|
||||
// NOTE: Only advance the tokeniser if we're at the end of the line and
|
||||
// there's more to tokenise.
|
||||
char const *end = tokeniser->string.data + tokeniser->string.size;
|
||||
if (tokeniser->it != end && tokeniser->end_of_line) {
|
||||
tokeniser->end_of_line = false;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static DN_Str8 DN_CSV_TokeniserNextField(DN_CSVTokeniser *tokeniser)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_CSV_TokeniserValid(tokeniser))
|
||||
return result;
|
||||
|
||||
if (!DN_Str8_HasData(tokeniser->string)) {
|
||||
tokeniser->bad = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: First time tokeniser is invoked with a string, set up initial state.
|
||||
char const *string_end = tokeniser->string.data + tokeniser->string.size;
|
||||
if (!tokeniser->it) {
|
||||
tokeniser->it = tokeniser->string.data;
|
||||
// NOTE: Skip any leading new lines
|
||||
while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r')
|
||||
if (++tokeniser->it == string_end)
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: Tokeniser pointing at end, no more valid data to parse.
|
||||
if (tokeniser->it == string_end)
|
||||
return result;
|
||||
|
||||
// NOTE: Scan forward until the next control character.
|
||||
// 1. '"' Double quoted field, extract everything between the quotes.
|
||||
// 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter.
|
||||
// 3. '\n' Last field in record, extract everything leading up the the new line.
|
||||
char const *begin = tokeniser->it;
|
||||
while (tokeniser->it != string_end && (tokeniser->it[0] != '"' &&
|
||||
tokeniser->it[0] != tokeniser->delimiter &&
|
||||
tokeniser->it[0] != '\n'))
|
||||
tokeniser->it++;
|
||||
|
||||
bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"';
|
||||
if (quoted_field) {
|
||||
begin = ++tokeniser->it; // Begin after the quote
|
||||
|
||||
// NOTE: Scan forward until the next '"' which marks the end
|
||||
// of the field unless it is escaped by another '"'.
|
||||
find_next_quote:
|
||||
while (tokeniser->it != string_end && tokeniser->it[0] != '"')
|
||||
tokeniser->it++;
|
||||
|
||||
// NOTE: If we encounter a '"' right after, the quotes were escaped
|
||||
// and we need to skip to the next instance of a '"'.
|
||||
if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') {
|
||||
tokeniser->it += 2;
|
||||
goto find_next_quote;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Mark the end of the field
|
||||
char const *end = tokeniser->it;
|
||||
tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n';
|
||||
|
||||
// NOTE: In files with \r\n style new lines ensure that we don't include
|
||||
// the \r byte in the CSV field we produce.
|
||||
if (end != string_end && end[0] == '\n') {
|
||||
DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data &&
|
||||
"Internal error: The string iterator is pointing behind the start of the string we're reading");
|
||||
if (end[-1] == '\r')
|
||||
end = end - 1;
|
||||
}
|
||||
|
||||
// NOTE: Quoted fields may have whitespace after the closing quote, we skip
|
||||
// until we reach the field terminator.
|
||||
if (quoted_field)
|
||||
while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter &&
|
||||
tokeniser->it[0] != '\n'))
|
||||
tokeniser->it++;
|
||||
|
||||
// NOTE: Advance the tokeniser past the field terminator.
|
||||
if (tokeniser->it != string_end)
|
||||
tokeniser->it++;
|
||||
|
||||
// NOTE: Generate the record
|
||||
result.data = DN_CAST(char *) begin;
|
||||
result.size = DN_CAST(int)(end - begin);
|
||||
return result;
|
||||
}
|
||||
|
||||
static DN_Str8 DN_CSV_TokeniserNextColumn(DN_CSVTokeniser *tokeniser)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_CSV_TokeniserValid(tokeniser))
|
||||
return result;
|
||||
|
||||
// NOTE: End of line, the user must explicitly advance to the next row
|
||||
if (tokeniser->end_of_line)
|
||||
return result;
|
||||
|
||||
// NOTE: Advance tokeniser to the next field in the row
|
||||
result = DN_CSV_TokeniserNextField(tokeniser);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_CSV_TokeniserSkipLine(DN_CSVTokeniser *tokeniser)
|
||||
{
|
||||
while (DN_CSV_TokeniserValid(tokeniser) && !tokeniser->end_of_line)
|
||||
DN_CSV_TokeniserNextColumn(tokeniser);
|
||||
DN_CSV_TokeniserNextRow(tokeniser);
|
||||
}
|
||||
|
||||
static int DN_CSV_TokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator)
|
||||
{
|
||||
if (!DN_CSV_TokeniserValid(tokeniser) || !fields || fields_size <= 0)
|
||||
return 0;
|
||||
|
||||
int result = 0;
|
||||
for (; result < fields_size; result++) {
|
||||
fields[result] = column_iterator ? DN_CSV_TokeniserNextColumn(tokeniser) : DN_CSV_TokeniserNextField(tokeniser);
|
||||
if (!DN_CSV_TokeniserValid(tokeniser) || !DN_Str8_HasData(fields[result]))
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(4505) // 'x': unreferenced function with internal linkage has been removed
|
||||
static int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size)
|
||||
{
|
||||
int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int DN_CSV_TokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size)
|
||||
{
|
||||
int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_CSV_TokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count)
|
||||
{
|
||||
for (int i = 0; i < count && DN_CSV_TokeniserValid(tokeniser); i++)
|
||||
DN_CSV_TokeniserSkipLine(tokeniser);
|
||||
}
|
||||
|
||||
static void DN_CSV_PackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value)
|
||||
{
|
||||
if (serialise == DN_CSVSerialise_Read) {
|
||||
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
|
||||
DN_Str8ToU64Result to_u64 = DN_Str8_ToU64(csv_value, 0);
|
||||
DN_Assert(to_u64.success);
|
||||
*value = to_u64.value;
|
||||
} else {
|
||||
DN_Str8Builder_AppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value);
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_CSV_PackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value)
|
||||
{
|
||||
if (serialise == DN_CSVSerialise_Read) {
|
||||
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
|
||||
DN_Str8ToI64Result to_i64 = DN_Str8_ToI64(csv_value, 0);
|
||||
DN_Assert(to_i64.success);
|
||||
*value = to_i64.value;
|
||||
} else {
|
||||
DN_Str8Builder_AppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value);
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_CSV_PackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value)
|
||||
{
|
||||
DN_I64 u64 = *value;
|
||||
DN_CSV_PackI64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = DN_SaturateCastI64ToI32(u64);
|
||||
}
|
||||
|
||||
static void DN_CSV_PackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value)
|
||||
{
|
||||
DN_I64 u64 = *value;
|
||||
DN_CSV_PackI64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = DN_SaturateCastI64ToI16(u64);
|
||||
}
|
||||
|
||||
static void DN_CSV_PackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value)
|
||||
{
|
||||
DN_I64 u64 = *value;
|
||||
DN_CSV_PackI64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = DN_SaturateCastI64ToI8(u64);
|
||||
}
|
||||
|
||||
|
||||
static void DN_CSV_PackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value)
|
||||
{
|
||||
DN_U64 u64 = *value;
|
||||
DN_CSV_PackU64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = DN_SaturateCastU64ToU32(u64);
|
||||
}
|
||||
|
||||
static void DN_CSV_PackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value)
|
||||
{
|
||||
DN_U64 u64 = *value;
|
||||
DN_CSV_PackU64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = DN_SaturateCastU64ToU16(u64);
|
||||
}
|
||||
|
||||
static void DN_CSV_PackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value)
|
||||
{
|
||||
DN_U64 u64 = *value;
|
||||
DN_CSV_PackU64(pack, serialise, &u64);
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*value = u64 ? 1 : 0;
|
||||
}
|
||||
|
||||
static void DN_CSV_PackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena)
|
||||
{
|
||||
if (serialise == DN_CSVSerialise_Read) {
|
||||
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
|
||||
*str8 = DN_Str8_Copy(arena, csv_value);
|
||||
} else {
|
||||
DN_Str8Builder_AppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_STR_FMT(*str8));
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_CSV_PackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size)
|
||||
{
|
||||
if (serialise == DN_CSVSerialise_Read) {
|
||||
DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser);
|
||||
*size = DN_Min(*size, csv_value.size);
|
||||
DN_Memcpy(dest, csv_value.data, *size);
|
||||
} else {
|
||||
DN_Str8Builder_AppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_CAST(int)(*size), dest);
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_CSV_PackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max)
|
||||
{
|
||||
if (serialise == DN_CSVSerialise_Read)
|
||||
*size = max;
|
||||
DN_CSV_PackBuffer(pack, serialise, dest, size);
|
||||
}
|
||||
|
||||
static bool DN_CSV_PackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise)
|
||||
{
|
||||
bool result = true;
|
||||
if (serialise == DN_CSVSerialise_Read) {
|
||||
result = DN_CSV_TokeniserNextRow(&pack->read_tokeniser);
|
||||
} else {
|
||||
pack->write_column = 0;
|
||||
result = DN_Str8Builder_AppendRef(&pack->write_builder, DN_STR8("\n"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
DN_MSVC_WARNING_POP
|
26
Extra/dn_csv.h
Normal file
26
Extra/dn_csv.h
Normal file
@ -0,0 +1,26 @@
|
||||
#if !defined(DN_CSV_H)
|
||||
#define DN_CSV_H
|
||||
|
||||
enum DN_CSVSerialise
|
||||
{
|
||||
DN_CSVSerialise_Read,
|
||||
DN_CSVSerialise_Write,
|
||||
};
|
||||
|
||||
struct DN_CSVTokeniser
|
||||
{
|
||||
bool bad;
|
||||
DN_Str8 string;
|
||||
char delimiter;
|
||||
char const *it;
|
||||
bool end_of_line;
|
||||
};
|
||||
|
||||
struct DN_CSVPack
|
||||
{
|
||||
DN_Str8Builder write_builder;
|
||||
DN_USize write_column;
|
||||
DN_CSVTokeniser read_tokeniser;
|
||||
};
|
||||
|
||||
#endif // !defined(DN_CSV_H)
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#define DN_HASH_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,12 +12,12 @@
|
||||
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
||||
// \__| \__|\__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_hash.cpp
|
||||
// dn_hash.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$FNV1] DN_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_FNV1A //////////////////////////////////////////////////////////////////////////////////
|
||||
// Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/
|
||||
DN_API uint32_t DN_FNV1A32_Iterate(void const *bytes, DN_USize size, uint32_t hash)
|
||||
{
|
||||
@ -48,7 +47,7 @@ DN_API uint64_t DN_FNV1A64_Hash(void const *bytes, DN_USize size)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MMUR] DN_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_MurmurHash3 ////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_MMH3_ROTL32(x, y) _rotl(x, y)
|
||||
#define DN_MMH3_ROTL64(x, y) _rotl64(x, y)
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#if !defined(DN_HASH_H)
|
||||
#define DN_HASH_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,17 +13,12 @@
|
||||
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
||||
// \__| \__|\__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_hash.h -- Hashing functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$FNV1] DN_FNV1A -- Hash(x) -> 32/64bit via FNV1a
|
||||
// [$MMUR] DN_MurmurHash3 -- Hash(x) -> 32/128bit via MurmurHash3
|
||||
// dn_hash.h -- Hashing functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$FNV1] DN_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_FNV1A //////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_FNV1A32_SEED)
|
||||
#define DN_FNV1A32_SEED 2166136261U
|
||||
#endif
|
||||
@ -32,18 +27,19 @@
|
||||
#define DN_FNV1A64_SEED 14695981039346656037ULL
|
||||
#endif
|
||||
|
||||
// NOTE: [$MMUR] DN_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_MurmurHash3 ////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_MurmurHash3 { uint64_t e[2]; };
|
||||
|
||||
// NOTE: [$FNV1] DN_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_FNV1A //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API uint32_t DN_FNV1A32_Hash (void const *bytes, DN_USize size);
|
||||
DN_API uint64_t DN_FNV1A64_Hash (void const *bytes, DN_USize size);
|
||||
DN_API uint32_t DN_FNV1A32_Iterate (void const *bytes, DN_USize size, uint32_t hash);
|
||||
DN_API uint64_t DN_FNV1A64_Iterate (void const *bytes, DN_USize size, uint64_t hash);
|
||||
|
||||
// NOTE: [$MMUR] DN_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_MurmurHash3 ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API uint32_t DN_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed);
|
||||
DN_API DN_MurmurHash3 DN_MurmurHash3_x64U128 (void const *key, int len, uint32_t seed);
|
||||
#define DN_MurmurHash3_x64U128AsU64(key, len, seed) (DN_MurmurHash3_x64U128(key, len, seed).e[0])
|
||||
#define DN_MurmurHash3_x64U128AsU32(key, len, seed) (DN_CAST(uint32_t)DN_MurmurHash3_x64U128(key, len, seed).e[0])
|
||||
|
||||
#endif // !defined(DN_HASH_H)
|
374
Extra/dn_helpers.cpp
Normal file
374
Extra/dn_helpers.cpp
Normal file
@ -0,0 +1,374 @@
|
||||
#define DN_HELPERS_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
||||
//
|
||||
// dn_helpers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: DN_PCG32 //////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL
|
||||
#define DN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL
|
||||
|
||||
DN_API DN_PCG32 DN_PCG32_Init(uint64_t seed)
|
||||
{
|
||||
DN_PCG32 result = {};
|
||||
DN_PCG32_Next(&result);
|
||||
result.state += seed;
|
||||
DN_PCG32_Next(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint32_t DN_PCG32_Next(DN_PCG32 *rng)
|
||||
{
|
||||
uint64_t state = rng->state;
|
||||
rng->state = state * DN_PCG_DEFAULT_MULTIPLIER_64 + DN_PCG_DEFAULT_INCREMENT_64;
|
||||
|
||||
// XSH-RR
|
||||
uint32_t value = (uint32_t)((state ^ (state >> 18)) >> 27);
|
||||
int rot = state >> 59;
|
||||
return rot ? (value >> rot) | (value << (32 - rot)) : value;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_PCG32_Next64(DN_PCG32 *rng)
|
||||
{
|
||||
uint64_t value = DN_PCG32_Next(rng);
|
||||
value <<= 32;
|
||||
value |= DN_PCG32_Next(rng);
|
||||
return value;
|
||||
}
|
||||
|
||||
DN_API uint32_t DN_PCG32_Range(DN_PCG32 *rng, uint32_t low, uint32_t high)
|
||||
{
|
||||
uint32_t bound = high - low;
|
||||
uint32_t threshold = -(int32_t)bound % bound;
|
||||
|
||||
for (;;) {
|
||||
uint32_t r = DN_PCG32_Next(rng);
|
||||
if (r >= threshold)
|
||||
return low + (r % bound);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API float DN_PCG32_NextF32(DN_PCG32 *rng)
|
||||
{
|
||||
uint32_t x = DN_PCG32_Next(rng);
|
||||
return (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
}
|
||||
|
||||
DN_API double DN_PCG32_NextF64(DN_PCG32 *rng)
|
||||
{
|
||||
uint64_t x = DN_PCG32_Next64(rng);
|
||||
return (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
DN_API void DN_PCG32_Advance(DN_PCG32 *rng, uint64_t delta)
|
||||
{
|
||||
uint64_t cur_mult = DN_PCG_DEFAULT_MULTIPLIER_64;
|
||||
uint64_t cur_plus = DN_PCG_DEFAULT_INCREMENT_64;
|
||||
|
||||
uint64_t acc_mult = 1;
|
||||
uint64_t acc_plus = 0;
|
||||
|
||||
while (delta != 0) {
|
||||
if (delta & 1) {
|
||||
acc_mult *= cur_mult;
|
||||
acc_plus = acc_plus * cur_mult + cur_plus;
|
||||
}
|
||||
cur_plus = (cur_mult + 1) * cur_plus;
|
||||
cur_mult *= cur_mult;
|
||||
delta >>= 1;
|
||||
}
|
||||
|
||||
rng->state = acc_mult * rng->state + acc_plus;
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_JSON_BUILDER)
|
||||
// NOTE: DN_JSONBuilder ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent)
|
||||
{
|
||||
DN_JSONBuilder result = {};
|
||||
result.spaces_per_indent = spaces_per_indent;
|
||||
result.string_builder.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = DN_Str8Builder_Build(&builder->string_builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
|
||||
{
|
||||
if (key.size == 0 && value.size == 0)
|
||||
return;
|
||||
|
||||
DN_JSONBuilderItem item = DN_JSONBuilderItem_KeyValue;
|
||||
if (value.size >= 1) {
|
||||
if (value.data[0] == '{' || value.data[0] == '[')
|
||||
item = DN_JSONBuilderItem_OpenContainer;
|
||||
else if (value.data[0] == '}' || value.data[0] == ']')
|
||||
item = DN_JSONBuilderItem_CloseContainer;
|
||||
}
|
||||
|
||||
bool adding_to_container_with_items =
|
||||
item != DN_JSONBuilderItem_CloseContainer && (builder->last_item == DN_JSONBuilderItem_KeyValue ||
|
||||
builder->last_item == DN_JSONBuilderItem_CloseContainer);
|
||||
|
||||
uint8_t prefix_size = 0;
|
||||
char prefix[2] = {0};
|
||||
if (adding_to_container_with_items)
|
||||
prefix[prefix_size++] = ',';
|
||||
|
||||
if (builder->last_item != DN_JSONBuilderItem_Empty)
|
||||
prefix[prefix_size++] = '\n';
|
||||
|
||||
if (item == DN_JSONBuilderItem_CloseContainer)
|
||||
builder->indent_level--;
|
||||
|
||||
int spaces_per_indent = builder->spaces_per_indent ? builder->spaces_per_indent : 2;
|
||||
int spaces = builder->indent_level * spaces_per_indent;
|
||||
|
||||
if (key.size)
|
||||
DN_Str8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c\"%.*s\": %.*s",
|
||||
prefix_size,
|
||||
prefix,
|
||||
spaces,
|
||||
' ',
|
||||
DN_STR_FMT(key),
|
||||
DN_STR_FMT(value));
|
||||
else if (spaces == 0)
|
||||
DN_Str8Builder_AppendF(&builder->string_builder, "%.*s%.*s", prefix_size, prefix, DN_STR_FMT(value));
|
||||
else
|
||||
DN_Str8Builder_AppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DN_STR_FMT(value));
|
||||
|
||||
if (item == DN_JSONBuilderItem_OpenContainer)
|
||||
builder->indent_level++;
|
||||
|
||||
builder->last_item = item;
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(builder->string_builder.arena);
|
||||
DN_Str8 value = DN_Str8_InitFV(tmem.arena, value_fmt, args);
|
||||
DN_JSONBuilder_KeyValue(builder, key, value);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_KeyValueF(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, value_fmt);
|
||||
DN_JSONBuilder_KeyValueFV(builder, key, value_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_ObjectBeginNamed(DN_JSONBuilder *builder, DN_Str8 name)
|
||||
{
|
||||
DN_JSONBuilder_KeyValue(builder, name, DN_STR8("{"));
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_ObjectEnd(DN_JSONBuilder *builder)
|
||||
{
|
||||
DN_JSONBuilder_KeyValue(builder, DN_STR8(""), DN_STR8("}"));
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_ArrayBeginNamed(DN_JSONBuilder *builder, DN_Str8 name)
|
||||
{
|
||||
DN_JSONBuilder_KeyValue(builder, name, DN_STR8("["));
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_ArrayEnd(DN_JSONBuilder *builder)
|
||||
{
|
||||
DN_JSONBuilder_KeyValue(builder, DN_STR8(""), DN_STR8("]"));
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_Str8Named(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
|
||||
{
|
||||
DN_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_LiteralNamed(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value)
|
||||
{
|
||||
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_U64Named(DN_JSONBuilder *builder, DN_Str8 key, uint64_t value)
|
||||
{
|
||||
DN_JSONBuilder_KeyValueF(builder, key, "%I64u", value);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_I64Named(DN_JSONBuilder *builder, DN_Str8 key, int64_t value)
|
||||
{
|
||||
DN_JSONBuilder_KeyValueF(builder, key, "%I64d", value);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_F64Named(DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places)
|
||||
{
|
||||
if (!builder)
|
||||
return;
|
||||
|
||||
if (decimal_places >= 16)
|
||||
decimal_places = 16;
|
||||
|
||||
// NOTE: Generate the format string for the float, depending on how many
|
||||
// decimals places it wants.
|
||||
char float_fmt[16];
|
||||
if (decimal_places > 0) {
|
||||
// NOTE: Emit the format string "%.<decimal_places>f" i.e. %.1f
|
||||
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
|
||||
} else {
|
||||
// NOTE: Emit the format string "%f"
|
||||
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%f");
|
||||
}
|
||||
DN_JSONBuilder_KeyValueF(builder, key, float_fmt, value);
|
||||
}
|
||||
|
||||
DN_API void DN_JSONBuilder_BoolNamed(DN_JSONBuilder *builder, DN_Str8 key, bool value)
|
||||
{
|
||||
DN_Str8 value_string = value ? DN_STR8("true") : DN_STR8("false");
|
||||
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data);
|
||||
}
|
||||
#endif // !defined(DN_NO_JSON_BUILDER)
|
||||
|
||||
// NOTE: DN_JobQueue ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_JobQueueSPMC DN_OS_JobQueueSPMCInit()
|
||||
{
|
||||
DN_JobQueueSPMC result = {};
|
||||
result.thread_wait_for_job_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
result.wait_for_completion_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
result.complete_queue_write_semaphore = DN_OS_SemaphoreInit(DN_ArrayCountU(result.complete_queue));
|
||||
result.mutex = DN_OS_MutexInit();
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_JobQueueSPMCCanAdd(DN_JobQueueSPMC const *queue, uint32_t count)
|
||||
{
|
||||
uint32_t read_index = queue->read_index;
|
||||
uint32_t write_index = queue->write_index;
|
||||
uint32_t size = write_index - read_index;
|
||||
bool result = (size + count) <= DN_ArrayCountU(queue->jobs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_JobQueueSPMCAddArray(DN_JobQueueSPMC *queue, DN_Job *jobs, uint32_t count)
|
||||
{
|
||||
if (!queue)
|
||||
return false;
|
||||
|
||||
uint32_t const pot_mask = DN_ArrayCountU(queue->jobs) - 1;
|
||||
uint32_t read_index = queue->read_index;
|
||||
uint32_t write_index = queue->write_index;
|
||||
uint32_t size = write_index - read_index;
|
||||
|
||||
if ((size + count) > DN_ArrayCountU(queue->jobs))
|
||||
return false;
|
||||
|
||||
for (size_t offset = 0; offset < count; offset++) {
|
||||
uint32_t wrapped_write_index = (write_index + offset) & pot_mask;
|
||||
queue->jobs[wrapped_write_index] = jobs[offset];
|
||||
}
|
||||
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
queue->write_index += count;
|
||||
DN_OS_SemaphoreIncrement(&queue->thread_wait_for_job_semaphore, count);
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_JobQueueSPMCAdd(DN_JobQueueSPMC *queue, DN_Job job)
|
||||
{
|
||||
bool result = DN_OS_JobQueueSPMCAddArray(queue, &job, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API int32_t DN_OS_JobQueueSPMCThread(DN_OSThread *thread)
|
||||
{
|
||||
DN_JobQueueSPMC *queue = DN_CAST(DN_JobQueueSPMC *) thread->user_context;
|
||||
uint32_t const pot_mask = DN_ArrayCountU(queue->jobs) - 1;
|
||||
static_assert(DN_ArrayCountU(queue->jobs) == DN_ArrayCountU(queue->complete_queue), "PoT mask is used to mask access to both arrays");
|
||||
|
||||
for (;;) {
|
||||
DN_OS_SemaphoreWait(&queue->thread_wait_for_job_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
if (queue->quit)
|
||||
break;
|
||||
|
||||
DN_Assert(queue->read_index != queue->write_index);
|
||||
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
uint32_t wrapped_read_index = queue->read_index & pot_mask;
|
||||
DN_Job job = queue->jobs[wrapped_read_index];
|
||||
queue->read_index += 1;
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
|
||||
job.elapsed_tsc -= DN_CPU_TSC();
|
||||
job.func(thread, job.user_context);
|
||||
job.elapsed_tsc += DN_CPU_TSC();
|
||||
|
||||
if (job.add_to_completion_queue) {
|
||||
DN_OS_SemaphoreWait(&queue->complete_queue_write_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
queue->complete_queue[(queue->complete_write_index++ & pot_mask)] = job;
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
DN_OS_SemaphoreIncrement(&queue->complete_queue_write_semaphore, 1);
|
||||
}
|
||||
|
||||
// NOTE: Update finish counter
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
queue->finish_index += 1;
|
||||
|
||||
// NOTE: If all jobs are finished and we have another thread who is
|
||||
// blocked via `WaitForCompletion` for this job queue, we will go
|
||||
// release the semaphore to wake them all up.
|
||||
bool all_jobs_finished = queue->finish_index == queue->write_index;
|
||||
if (all_jobs_finished && queue->threads_waiting_for_completion) {
|
||||
DN_OS_SemaphoreIncrement(&queue->wait_for_completion_semaphore,
|
||||
queue->threads_waiting_for_completion);
|
||||
queue->threads_waiting_for_completion = 0;
|
||||
}
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
}
|
||||
|
||||
return queue->quit_exit_code;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_JobQueueSPMCWaitForCompletion(DN_JobQueueSPMC *queue)
|
||||
{
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
if (queue->finish_index == queue->write_index) {
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
return;
|
||||
}
|
||||
queue->threads_waiting_for_completion++;
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
|
||||
DN_OS_SemaphoreWait(&queue->wait_for_completion_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_OS_JobQueueSPMCGetFinishedJobs(DN_JobQueueSPMC *queue, DN_Job *jobs, DN_USize jobs_size)
|
||||
{
|
||||
DN_USize result = 0;
|
||||
if (!queue || !jobs || jobs_size <= 0)
|
||||
return result;
|
||||
|
||||
uint32_t const pot_mask = DN_ArrayCountU(queue->jobs) - 1;
|
||||
DN_OS_MutexLock(&queue->mutex);
|
||||
while (queue->complete_read_index < queue->complete_write_index && result < jobs_size)
|
||||
jobs[result++] = queue->complete_queue[(queue->complete_read_index++ & pot_mask)];
|
||||
DN_OS_MutexUnlock(&queue->mutex);
|
||||
|
||||
return result;
|
||||
}
|
335
Extra/dn_helpers.h
Normal file
335
Extra/dn_helpers.h
Normal file
@ -0,0 +1,335 @@
|
||||
#if !defined(DN_HELPERS_H)
|
||||
#define DN_HELPERS_H
|
||||
|
||||
#if !defined(DN_BASE_H)
|
||||
#error dn_base_inc.h must be included before this
|
||||
#endif
|
||||
|
||||
#if !defined(DN_MATH_H)
|
||||
#error dn_math.h must be included before this
|
||||
#endif
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
||||
//
|
||||
// dn_helpers.h -- Helper functions/data structures
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: DN_PCG32 //////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_PCG32 { uint64_t state; };
|
||||
|
||||
#if !defined(DN_NO_JSON_BUILDER)
|
||||
// NOTE: DN_JSONBuilder ////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_JSONBuilderItem
|
||||
{
|
||||
DN_JSONBuilderItem_Empty,
|
||||
DN_JSONBuilderItem_OpenContainer,
|
||||
DN_JSONBuilderItem_CloseContainer,
|
||||
DN_JSONBuilderItem_KeyValue,
|
||||
};
|
||||
|
||||
struct DN_JSONBuilder
|
||||
{
|
||||
bool use_stdout; // When set, ignore the string builder and dump immediately to stdout
|
||||
DN_Str8Builder string_builder; // (Internal)
|
||||
int indent_level; // (Internal)
|
||||
int spaces_per_indent; // The number of spaces per indent level
|
||||
DN_JSONBuilderItem last_item;
|
||||
};
|
||||
#endif // !defined(DN_NO_JSON_BUIDLER)
|
||||
|
||||
// NOTE: DN_BinarySearch ///////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
|
||||
template <typename T>
|
||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
|
||||
enum DN_BinarySearchType
|
||||
{
|
||||
// Index of the match. If no match is found, found is set to false and the
|
||||
// index is set to the index where the match should be inserted/exist, if
|
||||
// it were in the array
|
||||
DN_BinarySearchType_Match,
|
||||
|
||||
// Index of the first element in the array that is `element >= find`. If no such
|
||||
// item is found or the array is empty, then, the index is set to the array
|
||||
// size and found is set to `false`.
|
||||
//
|
||||
// For example:
|
||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
|
||||
// printf("%zu\n", result.index); // Prints index '4'
|
||||
|
||||
DN_BinarySearchType_LowerBound,
|
||||
|
||||
// Index of the first element in the array that is `element > find`. If no such
|
||||
// item is found or the array is empty, then, the index is set to the array
|
||||
// size and found is set to `false`.
|
||||
//
|
||||
// For example:
|
||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
|
||||
// printf("%zu\n", result.index); // Prints index '5'
|
||||
|
||||
DN_BinarySearchType_UpperBound,
|
||||
};
|
||||
|
||||
struct DN_BinarySearchResult
|
||||
{
|
||||
bool found;
|
||||
DN_USize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using DN_QSortLessThanProc = bool(T const &a, T const &b, void *user_context);
|
||||
|
||||
// NOTE: Misc //////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_JobQueue ///////////////////////////////////////////////////////////////////////////////
|
||||
typedef void(DN_JobQueueFunc)(DN_OSThread *thread, void *user_context);
|
||||
struct DN_Job
|
||||
{
|
||||
DN_JobQueueFunc *func; // The function to invoke for the job
|
||||
void *user_context; // Pointer user can set to use in their `job_func`
|
||||
uint64_t elapsed_tsc;
|
||||
uint16_t user_tag; // Arbitrary value the user can set to identiy the type of `user_context` this job has
|
||||
bool add_to_completion_queue; // When true, on job completion, job must be dequeued from the completion queue via `GetFinishedJobs`
|
||||
};
|
||||
|
||||
#if !defined(DN_JOB_QUEUE_SPMC_SIZE)
|
||||
#define DN_JOB_QUEUE_SPMC_SIZE 128
|
||||
#endif
|
||||
|
||||
struct DN_JobQueueSPMC
|
||||
{
|
||||
DN_OSMutex mutex;
|
||||
DN_OSSemaphore thread_wait_for_job_semaphore;
|
||||
DN_OSSemaphore wait_for_completion_semaphore;
|
||||
DN_U32 threads_waiting_for_completion;
|
||||
|
||||
DN_Job jobs[DN_JOB_QUEUE_SPMC_SIZE];
|
||||
DN_B32 quit;
|
||||
DN_U32 quit_exit_code;
|
||||
DN_U32 volatile read_index;
|
||||
DN_U32 volatile finish_index;
|
||||
DN_U32 volatile write_index;
|
||||
|
||||
DN_OSSemaphore complete_queue_write_semaphore;
|
||||
DN_Job complete_queue[DN_JOB_QUEUE_SPMC_SIZE];
|
||||
DN_U32 volatile complete_read_index;
|
||||
DN_U32 volatile complete_write_index;
|
||||
};
|
||||
|
||||
// NOTE: DN_PCG32 //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_PCG32 DN_PCG32_Init (uint64_t seed);
|
||||
DN_API uint32_t DN_PCG32_Next (DN_PCG32 *rng);
|
||||
DN_API uint64_t DN_PCG32_Next64 (DN_PCG32 *rng);
|
||||
DN_API uint32_t DN_PCG32_Range (DN_PCG32 *rng, uint32_t low, uint32_t high);
|
||||
DN_API DN_F32 DN_PCG32_NextF32 (DN_PCG32 *rng);
|
||||
DN_API DN_F64 DN_PCG32_NextF64 (DN_PCG32 *rng);
|
||||
DN_API void DN_PCG32_Advance (DN_PCG32 *rng, uint64_t delta);
|
||||
|
||||
#if !defined(DN_NO_JSON_BUILDER)
|
||||
// NOTE: DN_JSONBuilder ////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_JSONBuilder_Object(builder) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBegin(builder), \
|
||||
DN_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
DN_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_Array(builder) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBegin(builder), \
|
||||
DN_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
DN_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
|
||||
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);
|
||||
DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...);
|
||||
DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
||||
DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder);
|
||||
DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
||||
DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder);
|
||||
DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value);
|
||||
DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value);
|
||||
DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places);
|
||||
DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value);
|
||||
|
||||
#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_STR8(""))
|
||||
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_STR8(""))
|
||||
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_STR8(""), value)
|
||||
#endif // !defined(DN_NO_JSON_BUILDER)
|
||||
|
||||
// NOTE: DN_BinarySearch ///////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array,
|
||||
DN_USize array_size,
|
||||
T const &find,
|
||||
DN_BinarySearchType type = DN_BinarySearchType_Match,
|
||||
DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
||||
|
||||
// NOTE: DN_QSort //////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> void DN_QSort (T *array,
|
||||
DN_USize array_size,
|
||||
void *user_context,
|
||||
DN_QSortLessThanProc<T> less_than = DN_QSort_DefaultLessThan);
|
||||
|
||||
// NOTE: DN_JobQueue ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_JobQueueSPMC DN_OS_JobQueueSPMCInit ();
|
||||
DN_API bool DN_OS_JobQueueSPMCCanAdd (DN_JobQueueSPMC const *queue, uint32_t count);
|
||||
DN_API bool DN_OS_JobQueueSPMCAddArray (DN_JobQueueSPMC *queue, DN_Job *jobs, uint32_t count);
|
||||
DN_API bool DN_OS_JobQueueSPMCAdd (DN_JobQueueSPMC *queue, DN_Job job);
|
||||
DN_API void DN_OS_JobQueueSPMCWaitForCompletion (DN_JobQueueSPMC *queue);
|
||||
DN_API int32_t DN_OS_JobQueueSPMCThread (DN_OSThread *thread);
|
||||
DN_API DN_USize DN_OS_JobQueueSPMCGetFinishedJobs (DN_JobQueueSPMC *queue, DN_Job *jobs, DN_USize jobs_size);
|
||||
|
||||
// NOTE: DN_BinarySearch ///////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
||||
DN_USize array_size,
|
||||
T const &find,
|
||||
DN_BinarySearchType type,
|
||||
DN_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
DN_BinarySearchResult result = {};
|
||||
if (!array || array_size <= 0 || !less_than)
|
||||
return result;
|
||||
|
||||
T const *end = array + array_size;
|
||||
T const *first = array;
|
||||
T const *last = end;
|
||||
while (first != last) {
|
||||
DN_USize count = last - first;
|
||||
T const *it = first + (count / 2);
|
||||
|
||||
bool advance_first = false;
|
||||
if (type == DN_BinarySearchType_UpperBound)
|
||||
advance_first = !less_than(find, it[0]);
|
||||
else
|
||||
advance_first = less_than(it[0], find);
|
||||
|
||||
if (advance_first)
|
||||
first = it + 1;
|
||||
else
|
||||
last = it;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DN_BinarySearchType_Match: {
|
||||
result.found = first != end && !less_than(find, *first);
|
||||
} break;
|
||||
|
||||
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||
case DN_BinarySearchType_UpperBound: {
|
||||
result.found = first != end;
|
||||
} break;
|
||||
}
|
||||
|
||||
result.index = first - array;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_QSort //////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context)
|
||||
{
|
||||
(void)user_context;
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_QSort(T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc<T> less_than)
|
||||
{
|
||||
if (!array || array_size <= 1 || !less_than)
|
||||
return;
|
||||
|
||||
// NOTE: Insertion Sort, under 24->32 is an optimal amount /////////////////////////////////////
|
||||
const DN_USize QSORT_THRESHOLD = 24;
|
||||
if (array_size < QSORT_THRESHOLD) {
|
||||
for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) {
|
||||
for (DN_USize index = 0; index < item_to_insert_index; index++) {
|
||||
if (!less_than(array[index], array[item_to_insert_index], user_context)) {
|
||||
T item_to_insert = array[item_to_insert_index];
|
||||
for (DN_USize i = item_to_insert_index; i > index; i--)
|
||||
array[i] = array[i - 1];
|
||||
|
||||
array[index] = item_to_insert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Quick sort, under 24->32 is an optimal amount /////////////////////////////////////////
|
||||
DN_USize last_index = array_size - 1;
|
||||
DN_USize pivot_index = array_size / 2;
|
||||
DN_USize partition_index = 0;
|
||||
DN_USize start_index = 0;
|
||||
|
||||
// Swap pivot with last index, so pivot is always at the end of the array.
|
||||
// This makes logic much simpler.
|
||||
DN_Swap(array[last_index], array[pivot_index]);
|
||||
pivot_index = last_index;
|
||||
|
||||
// 4^, 8, 7, 5, 2, 3, 6
|
||||
if (less_than(array[start_index], array[pivot_index], user_context))
|
||||
partition_index++;
|
||||
start_index++;
|
||||
|
||||
// 4, |8, 7, 5^, 2, 3, 6*
|
||||
// 4, 5, |7, 8, 2^, 3, 6*
|
||||
// 4, 5, 2, |8, 7, ^3, 6*
|
||||
// 4, 5, 2, 3, |7, 8, ^6*
|
||||
for (DN_USize index = start_index; index < last_index; index++) {
|
||||
if (less_than(array[index], array[pivot_index], user_context)) {
|
||||
DN_Swap(array[partition_index], array[index]);
|
||||
partition_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Move pivot to right of partition
|
||||
// 4, 5, 2, 3, |6, 8, ^7*
|
||||
DN_Swap(array[partition_index], array[pivot_index]);
|
||||
DN_QSort(array, partition_index, user_context, less_than);
|
||||
|
||||
// Skip the value at partion index since that is guaranteed to be sorted.
|
||||
// 4, 5, 2, 3, (x), 8, 7
|
||||
DN_USize one_after_partition_index = partition_index + 1;
|
||||
DN_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than);
|
||||
}
|
||||
|
||||
#endif // !defined(DN_HELPERS_H)
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#define DN_JSON_CPP
|
||||
|
||||
// NOTE: DN_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
void *DN_JSON_ArenaAllocFunc(void *user_data, size_t count)
|
||||
@ -60,7 +59,7 @@ bool DN_JSON_ItPushObjElement(DN_JSONIt *it, json_object_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DN_ASSERT(it->stack_count < DN_ARRAY_ICOUNT(it->stack));
|
||||
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
|
||||
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObjElement, element};
|
||||
return true;
|
||||
}
|
||||
@ -69,7 +68,7 @@ bool DN_JSON_ItPushObj(DN_JSONIt *it, json_object_s *obj)
|
||||
{
|
||||
if (!it || !obj)
|
||||
return false;
|
||||
DN_ASSERT(it->stack_count < DN_ARRAY_ICOUNT(it->stack));
|
||||
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
|
||||
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObj, obj};
|
||||
return true;
|
||||
}
|
||||
@ -78,7 +77,7 @@ bool DN_JSON_ItPushArrayElement(DN_JSONIt *it, json_array_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DN_ASSERT(it->stack_count < DN_ARRAY_ICOUNT(it->stack));
|
||||
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
|
||||
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArrayElement, element};
|
||||
return true;
|
||||
}
|
||||
@ -87,7 +86,7 @@ bool DN_JSON_ItPushArray(DN_JSONIt *it, json_value_s *value)
|
||||
{
|
||||
if (!it || !value || json_value_as_array(value) == nullptr)
|
||||
return false;
|
||||
DN_ASSERT(it->stack_count < DN_ARRAY_ICOUNT(it->stack));
|
||||
DN_Assert(it->stack_count < DN_ArrayCountI(it->stack));
|
||||
it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArray, value};
|
||||
return true;
|
||||
}
|
||||
@ -111,7 +110,7 @@ void DN_JSON_ItPop(DN_JSONIt *it)
|
||||
{
|
||||
if (!it)
|
||||
return;
|
||||
DN_ASSERT(it->stack_count > 0);
|
||||
DN_Assert(it->stack_count > 0);
|
||||
if (it->stack_count > 0)
|
||||
it->stack_count--;
|
||||
}
|
||||
@ -136,11 +135,11 @@ json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it)
|
||||
|
||||
if (result->type == json_type_array) {
|
||||
json_array_s *array = json_value_as_array(result);
|
||||
DN_ASSERT(array);
|
||||
DN_Assert(array);
|
||||
DN_JSON_ItPushArray(it, result);
|
||||
} else if (result->type == json_type_object) {
|
||||
json_object_s *obj = json_value_as_object(result);
|
||||
DN_ASSERT(obj);
|
||||
DN_Assert(obj);
|
||||
DN_JSON_ItPushObj(it, obj);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#if !defined(DN_JSON_H)
|
||||
#define DN_JSON_H
|
||||
|
||||
#if !defined(SHEREDOM_JSON_H_INCLUDED)
|
||||
#error Sheredom json.h (github.com/sheredom/json.h) must be included before this file
|
||||
@ -50,7 +50,7 @@ json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it);
|
||||
bool DN_JSON_ItNext(DN_JSONIt *it);
|
||||
|
||||
#define DN_JSON_ItPushCurrValueIterateThenPop(it) \
|
||||
for(void *DN_UNIQUE_NAME(ptr) = DN_JSON_ItPushCurrValue(it); DN_UNIQUE_NAME(ptr); DN_JSON_ItPop(it), DN_UNIQUE_NAME(ptr) = nullptr) \
|
||||
for(void *DN_UniqueName(ptr) = DN_JSON_ItPushCurrValue(it); DN_UniqueName(ptr); DN_JSON_ItPop(it), DN_UniqueName(ptr) = nullptr) \
|
||||
while (DN_JSON_ItNext(it))
|
||||
|
||||
// NOTE: DN_JSON_ItCurr /////////////////////////////////////////////////////////////////////
|
||||
@ -87,3 +87,5 @@ bool DN_JSON_ItValueToBool(DN_JSONIt *it);
|
||||
|
||||
#define DN_JSON_ItErrorUnknownKeyValue(it) DN_JSON_ItErrorUnknownKeyValue_(it, DN_CALL_SITE)
|
||||
void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site);
|
||||
|
||||
#endif // !defined(DN_JSON_H)
|
1585
Extra/dn_math.cpp
Normal file
1585
Extra/dn_math.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,37 +1,10 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
// $$$\ $$$ |$$ __$$\\__$$ __|$$ | $$ |
|
||||
// $$$$\ $$$$ |$$ / $$ | $$ | $$ | $$ |
|
||||
// $$\$$\$$ $$ |$$$$$$$$ | $$ | $$$$$$$$ |
|
||||
// $$ \$$$ $$ |$$ __$$ | $$ | $$ __$$ |
|
||||
// $$ |\$ /$$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | \_/ $$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// \__| \__|\__| \__| \__| \__| \__|
|
||||
//
|
||||
// dqn_math.h -- Basic math functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$VEC2] DN_V2F32, V2I32 -- DN_V2
|
||||
// [$VEC3] DN_V3F32, V3I32 -- DN_V3
|
||||
// [$VEC4] DN_V4F32, V4I32 -- DN_V4
|
||||
// [$MAT4] DN_M4 -- DN_M4
|
||||
// [$M2x3] DN_M2x3 --
|
||||
// [$RECT] DN_Rect -- DN_RECT
|
||||
// [$MATH] Other --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
#if !defined(DN_MATH_H)
|
||||
#define DN_MATH_H
|
||||
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
#if !defined(DN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V2 /////////////////////////////////////////////////////////////////////////////////////
|
||||
union DN_V2I32
|
||||
{
|
||||
struct { int32_t x, y; };
|
||||
@ -55,7 +28,7 @@ union DN_V2F32
|
||||
#endif // !defined(DN_NO_V2)
|
||||
|
||||
#if !defined(DN_NO_V3)
|
||||
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V3 /////////////////////////////////////////////////////////////////////////////////////
|
||||
union DN_V3F32
|
||||
{
|
||||
struct { DN_F32 x, y, z; };
|
||||
@ -66,7 +39,7 @@ union DN_V3F32
|
||||
#endif // !defined(DN_NO_V3)
|
||||
|
||||
#if !defined(DN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V4 /////////////////////////////////////////////////////////////////////////////////////
|
||||
union DN_V4F32
|
||||
{
|
||||
struct { DN_F32 x, y, z, w; };
|
||||
@ -81,21 +54,21 @@ union DN_V4F32
|
||||
DN_MSVC_WARNING_POP
|
||||
|
||||
#if !defined(DN_NO_M4)
|
||||
// NOTE: [$MAT4] DN_M4 ////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_M4 /////////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_M4
|
||||
{
|
||||
DN_F32 columns[4][4]; // Column major matrix
|
||||
};
|
||||
#endif // !defined(DN_M4)
|
||||
|
||||
// NOTE: [$M2x3] DN_M2x3 //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_M2x3 ///////////////////////////////////////////////////////////////////////////////////
|
||||
union DN_M2x3
|
||||
{
|
||||
DN_F32 e[6];
|
||||
DN_F32 row[2][3];
|
||||
};
|
||||
|
||||
// NOTE: [$RECT] DN_Rect //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Rect ///////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_RECT)
|
||||
#if defined(DN_NO_V2)
|
||||
#error "Rectangles requires V2, DN_NO_V2 must not be defined"
|
||||
@ -131,7 +104,7 @@ struct DN_RectCut
|
||||
};
|
||||
#endif // !defined(DN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: API
|
||||
struct DN_RaycastLineIntersectV2Result
|
||||
{
|
||||
@ -141,7 +114,7 @@ struct DN_RaycastLineIntersectV2Result
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V2 /////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_V2I32_Zero DN_LITERAL(DN_V2I32){{(int32_t)(0), (int32_t)(0)}}
|
||||
#define DN_V2I32_One DN_LITERAL(DN_V2I32){{(int32_t)(1), (int32_t)(1)}}
|
||||
#define DN_V2I32_Init1N(x) DN_LITERAL(DN_V2I32){{(int32_t)(x), (int32_t)(x)}}
|
||||
@ -258,21 +231,21 @@ DN_API DN_V2F32 & operator+= (DN_V2F32& lhs
|
||||
DN_API DN_V2F32 & operator+= (DN_V2F32& lhs, DN_F32 rhs);
|
||||
DN_API DN_V2F32 & operator+= (DN_V2F32& lhs, int32_t rhs);
|
||||
|
||||
DN_API DN_V2F32 DN_V2_Min (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_V2F32 DN_V2_Max (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_V2F32 DN_V2_Abs (DN_V2F32 a);
|
||||
DN_API DN_F32 DN_V2_Dot (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_F32 DN_V2_LengthSq_V2x2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
||||
DN_API DN_F32 DN_V2_Length_V2x2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
||||
DN_API DN_F32 DN_V2_LengthSq (DN_V2F32 lhs);
|
||||
DN_API DN_F32 DN_V2_Length (DN_V2F32 lhs);
|
||||
DN_API DN_V2F32 DN_V2_Normalise (DN_V2F32 a);
|
||||
DN_API DN_V2F32 DN_V2_Perpendicular (DN_V2F32 a);
|
||||
DN_API DN_V2F32 DN_V2_Reflect (DN_V2F32 in, DN_V2F32 surface);
|
||||
DN_API DN_F32 DN_V2_Area (DN_V2F32 a);
|
||||
DN_API DN_V2F32 DN_V2F32_Min (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_V2F32 DN_V2F32_Max (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_V2F32 DN_V2F32_Abs (DN_V2F32 a);
|
||||
DN_API DN_F32 DN_V2F32_Dot (DN_V2F32 a, DN_V2F32 b);
|
||||
DN_API DN_F32 DN_V2F32_LengthSq_V2x2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
||||
DN_API DN_F32 DN_V2F32_Length_V2x2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
||||
DN_API DN_F32 DN_V2F32_LengthSq (DN_V2F32 lhs);
|
||||
DN_API DN_F32 DN_V2F32_Length (DN_V2F32 lhs);
|
||||
DN_API DN_V2F32 DN_V2F32_Normalise (DN_V2F32 a);
|
||||
DN_API DN_V2F32 DN_V2F32_Perpendicular (DN_V2F32 a);
|
||||
DN_API DN_V2F32 DN_V2F32_Reflect (DN_V2F32 in, DN_V2F32 surface);
|
||||
DN_API DN_F32 DN_V2F32_Area (DN_V2F32 a);
|
||||
#endif // !defined(DN_NO_V2)
|
||||
#if !defined(DN_NO_V3)
|
||||
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V3 /////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_V3F32_Init1N(x) DN_LITERAL(DN_V3F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
||||
#define DN_V3F32_Init3F32(x, y, z) DN_LITERAL(DN_V3F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z)}}
|
||||
#define DN_V3F32_InitV2F32_1F32(xy, z) DN_LITERAL(DN_V3F32){{(DN_F32)(xy.x), (DN_F32)(xy.y), (DN_F32)(z)}}
|
||||
@ -305,7 +278,7 @@ DN_API DN_F32 DN_V3F32_Length (DN_V3F32 a);
|
||||
DN_API DN_V3F32 DN_V3F32_Normalise (DN_V3F32 a);
|
||||
#endif // !defined(DN_NO_V3)
|
||||
#if !defined(DN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_V4 /////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_V4F32_Init1N(x) DN_LITERAL(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
||||
#define DN_V4F32_Init4N(x, y, z, w) DN_LITERAL(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}}
|
||||
#define DN_V4F32_InitV3_1N(xyz, w) DN_LITERAL(DN_V4F32){{xyz.x, xyz.y, xyz.z, w}}
|
||||
@ -328,7 +301,7 @@ DN_API DN_V4F32 & operator-= (DN_V4F32 &lhs,
|
||||
DN_API DN_V4F32 & operator+= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
||||
#endif // !defined(DN_NO_V4)
|
||||
#if !defined(DN_NO_M4)
|
||||
// NOTE: [$MAT4] DN_M4 ////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_M4 /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_F32 DN_V4F32Dot (DN_V4F32 a, DN_V4F32 b);
|
||||
DN_API DN_M4 DN_M4_Identity ();
|
||||
DN_API DN_M4 DN_M4_ScaleF (DN_F32 x, DN_F32 y, DN_F32 z);
|
||||
@ -351,7 +324,7 @@ DN_API DN_M4 DN_M4_DivF (DN_M4 lhs, DN_
|
||||
DN_API DN_FStr8<256> DN_M4_ColumnMajorString (DN_M4 mat);
|
||||
#endif
|
||||
#endif // !defined(DN_NO_M4)
|
||||
// NOTE: [$M2x3] DN_M2x3 //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_M2x3 ///////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool operator== (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
|
||||
DN_API bool operator!= (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
|
||||
DN_API DN_M2x3 DN_M2x3_Identity ();
|
||||
@ -362,7 +335,7 @@ DN_API DN_M2x3 DN_M2x3_Mul (DN_M2x3 m1, DN
|
||||
DN_API DN_V2F32 DN_M2x3_Mul2F32 (DN_M2x3 m1, DN_F32 x, DN_F32 y);
|
||||
DN_API DN_V2F32 DN_M2x3_MulV2 (DN_M2x3 m1, DN_V2F32 v2);
|
||||
#if !defined(DN_NO_RECT)
|
||||
// NOTE: [$RECT] DN_Rect //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Rect ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_Rect_Init2V2(pos, size) DN_LITERAL(DN_Rect){(pos), (size)}
|
||||
#define DN_Rect_Init4N(x, y, w, h) DN_LITERAL(DN_Rect){DN_LITERAL(DN_V2F32){{x, y}}, DN_LITERAL(DN_V2F32){{w, h}}}
|
||||
|
||||
@ -405,7 +378,9 @@ DN_API DN_Rect DN_RectCut_Cut (DN_RectCut rec
|
||||
#define DN_RectCut_Top(rect) DN_LITERAL(DN_RectCut){rect, DN_RectCutSide_Top}
|
||||
#define DN_RectCut_Bottom(rect) DN_LITERAL(DN_RectCut){rect, DN_RectCutSide_Bottom}
|
||||
#endif // !defined(DN_NO_RECT)
|
||||
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_RaycastLineIntersectV2Result DN_Raycast_LineIntersectV2(DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b);
|
||||
DN_API DN_V2F32 DN_Lerp_V2F32 (DN_V2F32 a, DN_F32 t, DN_V2F32 b);
|
||||
DN_API DN_F32 DN_Lerp_F32 (DN_F32 a, DN_F32 t, DN_F32 b);
|
||||
|
||||
#endif // !defined(DN_MATH_H)
|
2466
Extra/dn_tests.cpp
Normal file
2466
Extra/dn_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
31
Extra/dn_tests_main.cpp
Normal file
31
Extra/dn_tests_main.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "../dn_base_inc.h"
|
||||
#include "../dn_os_inc.h"
|
||||
#include "../dn_core_inc.h"
|
||||
|
||||
#include "../dn_base_inc.cpp"
|
||||
#include "../dn_os_inc.cpp"
|
||||
#include "../dn_core_inc.cpp"
|
||||
|
||||
#include "../Extra/dn_math.h"
|
||||
#include "../Extra/dn_helpers.h"
|
||||
|
||||
#include "../Extra/dn_math.cpp"
|
||||
#include "../Extra/dn_helpers.cpp"
|
||||
|
||||
#define DN_UT_IMPLEMENTATION
|
||||
#include "../Standalone/dn_utest.h"
|
||||
#include "../Extra/dn_tests.cpp"
|
||||
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6262) // Function uses '29804' bytes of stack. Consider moving some data to heap.
|
||||
int main(int, char**)
|
||||
{
|
||||
DN_Core core = {};
|
||||
DN_OSCore os = {};
|
||||
|
||||
DN_OS_Init(&os, nullptr);
|
||||
DN_Core_Init(&core, DN_CoreOnInit_LogAllFeatures);
|
||||
DN_Tests_RunSuite(DN_TestsPrint_Yes);
|
||||
return 0;
|
||||
}
|
||||
DN_MSVC_WARNING_POP
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#define DN_TYPE_INFO_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,7 +12,7 @@
|
||||
// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ |
|
||||
// \__| \__| \__| \________| \______|\__| \__|\__| \______/
|
||||
//
|
||||
// dqn_type_info.cpp
|
||||
// dn_type_info.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#if !defined(DN_TYPE_INFO_H)
|
||||
#define DN_TYPE_INFO_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,49 +13,52 @@
|
||||
// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ |
|
||||
// \__| \__| \__| \________| \______|\__| \__|\__| \______/
|
||||
//
|
||||
// dqn_type_info.h -- C++ type introspection
|
||||
// dn_type_info.h -- C++ type introspection
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
enum DN_TypeKind
|
||||
{
|
||||
DN_TypeKind_Nil,
|
||||
DN_TypeKind_Basic,
|
||||
DN_TypeKind_Enum,
|
||||
DN_TypeKind_Struct,
|
||||
DN_TypeKind_Nil,
|
||||
DN_TypeKind_Basic,
|
||||
DN_TypeKind_Enum,
|
||||
DN_TypeKind_Struct,
|
||||
};
|
||||
|
||||
struct DN_TypeField
|
||||
{
|
||||
uint16_t index;
|
||||
DN_Str8 name;
|
||||
DN_Str8 label;
|
||||
DN_ISize value;
|
||||
DN_USize offset_of;
|
||||
DN_USize size_of;
|
||||
DN_USize align_of;
|
||||
DN_Str8 type_decl;
|
||||
uint32_t type_enum;
|
||||
bool is_pointer;
|
||||
uint16_t array_size;
|
||||
DN_TypeField const * array_size_field;
|
||||
uint16_t index;
|
||||
DN_Str8 name;
|
||||
DN_Str8 label;
|
||||
DN_ISize value;
|
||||
DN_USize offset_of;
|
||||
DN_USize size_of;
|
||||
DN_USize align_of;
|
||||
DN_Str8 type_decl;
|
||||
uint32_t type_enum;
|
||||
bool is_pointer;
|
||||
uint16_t array_size;
|
||||
DN_TypeField const *array_size_field;
|
||||
};
|
||||
|
||||
struct DN_TypeInfo
|
||||
{
|
||||
DN_Str8 name;
|
||||
DN_TypeKind kind;
|
||||
DN_USize size_of;
|
||||
DN_TypeField const *fields;
|
||||
uint16_t fields_count;
|
||||
DN_Str8 name;
|
||||
DN_TypeKind kind;
|
||||
DN_USize size_of;
|
||||
DN_TypeField const *fields;
|
||||
uint16_t fields_count;
|
||||
};
|
||||
|
||||
struct DN_TypeGetField
|
||||
{
|
||||
bool success;
|
||||
DN_USize index;
|
||||
DN_TypeField *field;
|
||||
bool success;
|
||||
DN_USize index;
|
||||
DN_TypeField *field;
|
||||
};
|
||||
|
||||
DN_TypeGetField DN_Type_GetField(DN_TypeInfo const *type_info, DN_Str8 name);
|
||||
|
||||
#endif // !defined(DN_TYPE_INFO_H)
|
||||
|
734
OS/dn_os.cpp
Normal file
734
OS/dn_os.cpp
Normal file
@ -0,0 +1,734 @@
|
||||
#define DN_OS_CPP
|
||||
|
||||
static DN_OSCore *g_dn_os_core_;
|
||||
|
||||
static void DN_OS_LOGEmitFromTypeTypeFV_(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Assert(user_data);
|
||||
DN_OSCore *core = DN_CAST(DN_OSCore *)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested ////////////////////////////////////////////////
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
if (core->log_to_file && !core->log_file.handle && !core->log_file.error) {
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_OS_PathF' Actual type: 'struct DN_Str8'.
|
||||
DN_Str8 log_path = DN_OS_PathF(tmem.arena, "%S/dn.log", DN_OS_EXEDir(tmem.arena));
|
||||
DN_MSVC_WARNING_POP
|
||||
core->log_file = DN_OS_FileOpen(log_path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_AppendOnly, nullptr);
|
||||
}
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
|
||||
DN_LOGStyle style = {};
|
||||
if (!core->log_no_colour) {
|
||||
style.colour = true;
|
||||
style.bold = DN_LOGBold_Yes;
|
||||
if (type.is_u32_enum) {
|
||||
switch (type.u32) {
|
||||
case DN_LOGType_Debug: {
|
||||
style.colour = false;
|
||||
style.bold = DN_LOGBold_No;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Info: {
|
||||
style.g = 0x87;
|
||||
style.b = 0xff;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Warning: {
|
||||
style.r = 0xff;
|
||||
style.g = 0xff;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Error: {
|
||||
style.r = 0xff;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_OSDateTime os_date = DN_OS_DateLocalTimeNow();
|
||||
DN_LOGDate log_date = {};
|
||||
log_date.year = os_date.year;
|
||||
log_date.month = os_date.month;
|
||||
log_date.day = os_date.day;
|
||||
log_date.hour = os_date.hour;
|
||||
log_date.minute = os_date.minutes;
|
||||
log_date.second = os_date.seconds;
|
||||
|
||||
char prefix_buffer[128] = {};
|
||||
DN_LOGPrefixSize prefix_size = DN_LOG_MakePrefix(style, type, call_site, log_date, prefix_buffer, sizeof(prefix_buffer));
|
||||
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
{
|
||||
DN_OS_FileWrite(&core->log_file, DN_Str8_Init(prefix_buffer, prefix_size.size), nullptr);
|
||||
DN_OS_FileWriteF(&core->log_file, nullptr, "%*s ", DN_CAST(int)prefix_size.padding, "");
|
||||
DN_OS_FileWriteFV(&core->log_file, nullptr, fmt, args_copy);
|
||||
DN_OS_FileWrite(&core->log_file, DN_STR8("\n"), nullptr);
|
||||
}
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
va_end(args_copy);
|
||||
|
||||
DN_OSPrintDest dest = (type.is_u32_enum && type.u32 == DN_LOGType_Error) ? DN_OSPrintDest_Err : DN_OSPrintDest_Out;
|
||||
DN_OS_Print(dest, DN_Str8_Init(prefix_buffer, prefix_size.size));
|
||||
DN_OS_PrintF(dest, "%*s ", DN_CAST(int)prefix_size.padding, "");
|
||||
DN_OS_PrintLnFV(dest, fmt, args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_Init(DN_OSCore *os, DN_OSInitArgs *args)
|
||||
{
|
||||
g_dn_os_core_ = os;
|
||||
|
||||
// NOTE: OS
|
||||
{
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
SYSTEM_INFO system_info = {};
|
||||
GetSystemInfo(&system_info);
|
||||
os->page_size = system_info.dwPageSize;
|
||||
os->alloc_granularity = system_info.dwAllocationGranularity;
|
||||
QueryPerformanceFrequency(&os->win32_qpc_frequency);
|
||||
|
||||
HMODULE module = LoadLibraryA("kernel32.dll");
|
||||
os->win32_set_thread_description = DN_CAST(DN_WinSetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
|
||||
FreeLibrary(module);
|
||||
#else
|
||||
// TODO(doyle): Get the proper page size from the OS.
|
||||
os->page_size = DN_Kilobytes(4);
|
||||
os->alloc_granularity = DN_Kilobytes(64);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: Setup logging
|
||||
DN_OS_EmitLogsWithOSPrintFunctions(os);
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
// NOTE: win32 bcrypt
|
||||
{
|
||||
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
|
||||
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&os->win32_bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
|
||||
if (os->win32_bcrypt_rng_handle && init_status == 0)
|
||||
os->win32_bcrypt_init_success = true;
|
||||
else
|
||||
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE: Initialise tmem arenas which allocate memory and will be
|
||||
// recorded to the now initialised allocation table. The initialisation
|
||||
// of tmem memory may request tmem memory itself in leak tracing mode.
|
||||
// This is supported as the tmem arenas defer allocation tracking until
|
||||
// initialisation is done.
|
||||
DN_OSTLSInitArgs tls_init_args = {};
|
||||
if (args) {
|
||||
tls_init_args.commit = args->tls_commit;
|
||||
tls_init_args.reserve = args->tls_reserve;
|
||||
tls_init_args.err_sink_reserve = args->tls_err_sink_reserve;
|
||||
tls_init_args.err_sink_commit = args->tls_err_sink_commit;
|
||||
}
|
||||
|
||||
DN_OS_TLSInit(&os->tls, tls_init_args);
|
||||
DN_OS_TLSSetCurrentThreadTLS(&os->tls);
|
||||
os->cpu_report = DN_CPU_Report();
|
||||
|
||||
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_STR8(#label)};
|
||||
DN_CPU_FEAT_XMACRO
|
||||
#undef DN_CPU_FEAT_XENTRY
|
||||
|
||||
DN_Assert(g_dn_os_core_);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os)
|
||||
{
|
||||
DN_Assert(os);
|
||||
DN_LOG_SetEmitFromTypeFVFunc(DN_OS_LOGEmitFromTypeTypeFV_, os);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_DumpThreadContextArenaStat(DN_Str8 file_path)
|
||||
{
|
||||
#if defined(DN_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) {
|
||||
DN_LOG_ErrorF("Failed to dump thread context arenas [file=%.*s]", DN_STR_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.
|
||||
DN_ArenaStat stats[DN_CArray_CountI(g_dn_core->thread_context_arena_stats)];
|
||||
int stats_size = 0;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->thread_context_mutex);
|
||||
stats_size = g_dn_core->thread_context_arena_stats_count;
|
||||
DN_Memcpy(stats, g_dn_core->thread_context_arena_stats, sizeof(stats[0]) * stats_size);
|
||||
DN_TicketMutex_End(&g_dn_core->thread_context_mutex);
|
||||
|
||||
// NOTE: Print the cumulative stat
|
||||
DN_DateHMSTimeStr now = DN_Date_HMSLocalTimeStrNow();
|
||||
fprintf(file,
|
||||
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||
now.date_size,
|
||||
now.date,
|
||||
now.hms_size,
|
||||
now.hms,
|
||||
g_dn_core->thread_context_arena_stats_count);
|
||||
|
||||
// NOTE: Write the cumulative thread arena data
|
||||
{
|
||||
DN_ArenaStat stat = {};
|
||||
for (DN_USize index = 0; index < stats_size; index++) {
|
||||
DN_ArenaStat const *current = stats + index;
|
||||
stat.capacity += current->capacity;
|
||||
stat.used += current->used;
|
||||
stat.wasted += current->wasted;
|
||||
stat.blocks += current->blocks;
|
||||
|
||||
stat.capacity_hwm = DN_Max(stat.capacity_hwm, current->capacity_hwm);
|
||||
stat.used_hwm = DN_Max(stat.used_hwm, current->used_hwm);
|
||||
stat.wasted_hwm = DN_Max(stat.wasted_hwm, current->wasted_hwm);
|
||||
stat.blocks_hwm = DN_Max(stat.blocks_hwm, current->blocks_hwm);
|
||||
}
|
||||
|
||||
DN_ArenaStatStr stats_string = DN_Arena_StatStr(&stat);
|
||||
fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data);
|
||||
}
|
||||
|
||||
// NOTE: Print individual thread arena data
|
||||
for (DN_USize index = 0; index < stats_size; index++) {
|
||||
DN_ArenaStat const *current = stats + index;
|
||||
DN_ArenaStatStr current_string = DN_Arena_StatStr(current);
|
||||
fprintf(file, " [%03d] CURR %.*s\n", DN_CAST(int) index, current_string.size, current_string.data);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
DN_LOG_InfoF("Dumped thread context arenas [file=%.*s]", DN_STR_FMT(file_path));
|
||||
#else
|
||||
(void)file_path;
|
||||
#endif // #if defined(DN_DEBUG_THREAD_CONTEXT)
|
||||
}
|
||||
|
||||
// NOTE: Date //////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8(DN_OSDateTime time, char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTimeStr8 result = {};
|
||||
result.hms_size = DN_CAST(uint8_t) DN_SNPrintF(result.hms,
|
||||
DN_ArrayCountI(result.hms),
|
||||
"%02hhu%c%02hhu%c%02hhu",
|
||||
time.hour,
|
||||
hms_separator,
|
||||
time.minutes,
|
||||
hms_separator,
|
||||
time.seconds);
|
||||
|
||||
result.date_size = DN_CAST(uint8_t) DN_SNPrintF(result.date,
|
||||
DN_ArrayCountI(result.date),
|
||||
"%hu%c%02hhu%c%02hhu",
|
||||
time.year,
|
||||
date_separator,
|
||||
time.month,
|
||||
date_separator,
|
||||
time.day);
|
||||
|
||||
DN_Assert(result.hms_size < DN_ArrayCountU(result.hms));
|
||||
DN_Assert(result.date_size < DN_ArrayCountU(result.date));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTime time = DN_OS_DateLocalTimeNow();
|
||||
DN_OSDateTimeStr8 result = DN_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_OS_DateUnixTimeS()
|
||||
{
|
||||
uint64_t result = DN_OS_DateUnixTimeNs() / (1'000 /*us*/ * 1'000 /*ms*/ * 1'000 /*s*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_DateIsValid(DN_OSDateTime date)
|
||||
{
|
||||
if (date.year < 1970)
|
||||
return false;
|
||||
if (date.month <= 0 || date.month >= 13)
|
||||
return false;
|
||||
if (date.day <= 0 || date.day >= 32)
|
||||
return false;
|
||||
if (date.hour >= 24)
|
||||
return false;
|
||||
if (date.minutes >= 60)
|
||||
return false;
|
||||
if (date.seconds >= 60)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_Str8 exe_path = DN_OS_EXEPath(tmem.arena);
|
||||
DN_Str8 separators[] = {DN_STR8("/"), DN_STR8("\\")};
|
||||
DN_Str8BinarySplitResult split = DN_Str8_BinarySplitLastArray(exe_path, separators, DN_ArrayCountU(separators));
|
||||
result = DN_Str8_Copy(arena, split.lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = ticks / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin()
|
||||
{
|
||||
DN_OSTimer result = {};
|
||||
result.start = DN_OS_PerfCounterNow();
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TimerEnd(DN_OSTimer *timer)
|
||||
{
|
||||
timer->end = DN_OS_PerfCounterNow();
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerS(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterS(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerMs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterMs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerUs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterUs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerNs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterNs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency)
|
||||
{
|
||||
uint64_t os_frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL;
|
||||
uint64_t tsc_begin = DN_CPU_TSC();
|
||||
uint64_t result = 0;
|
||||
if (tsc_begin) {
|
||||
uint64_t os_elapsed = 0;
|
||||
for (uint64_t os_begin = DN_OS_PerfCounterNow(); os_elapsed < os_target_elapsed;)
|
||||
os_elapsed = DN_OS_PerfCounterNow() - os_begin;
|
||||
uint64_t tsc_end = DN_CPU_TSC();
|
||||
uint64_t tsc_elapsed = tsc_end - tsc_begin;
|
||||
result = tsc_elapsed / os_elapsed * os_frequency;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: DN_OSPathInfo/File ////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against)
|
||||
{
|
||||
DN_OSPathInfo file_info = DN_OS_PathInfo(file);
|
||||
DN_OSPathInfo check_against_info = DN_OS_PathInfo(check_against);
|
||||
bool result = !file_info.exists || file_info.last_write_time_in_s < check_against_info.last_write_time_in_s;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWrite(DN_OSFile *file, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
bool result = DN_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct DN_OSFileWriteChunker_
|
||||
{
|
||||
DN_OSErrSink *err;
|
||||
DN_OSFile *file;
|
||||
bool success;
|
||||
};
|
||||
|
||||
static char *DN_OS_FileWriteChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
DN_OSFileWriteChunker_ *chunker = DN_CAST(DN_OSFileWriteChunker_ *)user;
|
||||
chunker->success = DN_OS_FileWritePtr(chunker->file, buf, len, chunker->err);
|
||||
char *result = chunker->success ? DN_CAST(char *) buf : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteFV(DN_OSFile *file, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!file || !fmt)
|
||||
return result;
|
||||
|
||||
DN_OSFileWriteChunker_ chunker = {};
|
||||
chunker.err = error;
|
||||
chunker.file = file;
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)(DN_OS_FileWriteChunker_, &chunker, buffer, fmt, args);
|
||||
|
||||
result = chunker.success;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteF(DN_OSFile *file, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_FileWriteFV(file, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll(DN_Arena *arena, DN_Str8 path, DN_OSErrSink *error)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
// NOTE: Query file size + allocate buffer /////////////////////////////////////////////////////
|
||||
DN_OSPathInfo path_info = DN_OS_PathInfo(path);
|
||||
if (!path_info.exists) {
|
||||
DN_OS_ErrSinkAppendF(error, 1, "File does not exist/could not be queried for reading '%.*s'", DN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_ArenaTempMem temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
result = DN_Str8_Alloc(arena, path_info.size, DN_ZeroMem_No);
|
||||
if (!DN_Str8_HasData(result)) {
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer_size_str8 = DN_CVT_U64ToByteSizeStr8(tmem.arena, path_info.size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_OS_ErrSinkAppendF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_STR_FMT(buffer_size_str8), DN_STR_FMT(path));
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Read the file from disk ///////////////////////////////////////////////////////////////
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_Read, error);
|
||||
DN_OSFileRead read = DN_OS_FileRead(&file, result.data, result.size, error);
|
||||
if (file.error || !read.success) {
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
}
|
||||
DN_OS_FileClose(&file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAll(DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_Write, error);
|
||||
bool result = DN_OS_FileWrite(&file, buffer, error);
|
||||
DN_OS_FileClose(&file);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllFV(DN_Str8 file_path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAll(file_path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllF(DN_Str8 file_path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllFV(file_path, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 tmp_path = DN_Str8_InitF(tmem.arena, "%.*s.tmp", DN_STR_FMT(path));
|
||||
if (!DN_OS_WriteAll(tmp_path, buffer, error))
|
||||
return false;
|
||||
if (!DN_OS_CopyFile(tmp_path, path, true /*overwrite*/, error))
|
||||
return false;
|
||||
if (!DN_OS_PathDelete(tmp_path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeFV(DN_Str8 path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAllSafe(path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeF(DN_Str8 path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllSafeFV(path, error, fmt, args);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: DN_OSPath /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
if (!arena || !fs_path || !DN_Str8_HasData(path))
|
||||
return false;
|
||||
|
||||
if (path.size <= 0)
|
||||
return true;
|
||||
|
||||
DN_Str8 const delimiter_array[] = {
|
||||
DN_STR8("\\"),
|
||||
DN_STR8("/")};
|
||||
|
||||
if (fs_path->links_size == 0)
|
||||
fs_path->has_prefix_path_separator = (path.data[0] == '/');
|
||||
|
||||
for (;;) {
|
||||
DN_Str8BinarySplitResult delimiter = DN_Str8_BinarySplitArray(path, delimiter_array, DN_ArrayCountU(delimiter_array));
|
||||
for (; delimiter.lhs.data; delimiter = DN_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DN_ArrayCountU(delimiter_array))) {
|
||||
if (delimiter.lhs.size <= 0)
|
||||
continue;
|
||||
|
||||
DN_OSPathLink *link = DN_Arena_New(arena, DN_OSPathLink, DN_ZeroMem_Yes);
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
link->string = delimiter.lhs;
|
||||
link->prev = fs_path->tail;
|
||||
if (fs_path->tail)
|
||||
fs_path->tail->next = link;
|
||||
else
|
||||
fs_path->head = link;
|
||||
fs_path->tail = link;
|
||||
fs_path->links_size += 1;
|
||||
fs_path->string_size += delimiter.lhs.size;
|
||||
}
|
||||
|
||||
if (!delimiter.lhs.data)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAdd(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 copy = DN_Str8_Copy(arena, path);
|
||||
bool result = DN_Str8_HasData(copy) ? true : DN_OS_PathAddRef(arena, fs_path, copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAddF(DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(arena, fmt, args);
|
||||
va_end(args);
|
||||
bool result = DN_OS_PathAddRef(arena, fs_path, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathPop(DN_OSPath *fs_path)
|
||||
{
|
||||
if (!fs_path)
|
||||
return false;
|
||||
|
||||
if (fs_path->tail) {
|
||||
DN_Assert(fs_path->head);
|
||||
fs_path->links_size -= 1;
|
||||
fs_path->string_size -= fs_path->tail->string.size;
|
||||
fs_path->tail = fs_path->tail->prev;
|
||||
if (fs_path->tail)
|
||||
fs_path->tail->next = nullptr;
|
||||
else
|
||||
fs_path->head = nullptr;
|
||||
} else {
|
||||
DN_Assert(!fs_path->head);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathTo(DN_Arena *arena, DN_Str8 path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_OSPath fs_path = {};
|
||||
DN_OS_PathAddRef(arena, &fs_path, path);
|
||||
DN_Str8 result = DN_OS_PathBuildWithSeparator(arena, &fs_path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathToF(DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_Path(DN_Arena *arena, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, DN_OSPathSeperatorString);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathF(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_Path(arena, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator(DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!fs_path || fs_path->links_size <= 0)
|
||||
return result;
|
||||
|
||||
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
|
||||
DN_USize string_size = (fs_path->has_prefix_path_separator ? path_separator.size : 0) + fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size);
|
||||
result = DN_Str8_Alloc(arena, string_size, DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
char *dest = result.data;
|
||||
if (fs_path->has_prefix_path_separator) {
|
||||
DN_Memcpy(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
|
||||
for (DN_OSPathLink *link = fs_path->head; link; link = link->next) {
|
||||
DN_Str8 string = link->string;
|
||||
DN_Memcpy(dest, string.data, string.size);
|
||||
dest += string.size;
|
||||
|
||||
if (link != fs_path->tail) {
|
||||
DN_Memcpy(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.data[string_size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSExecResult DN_OS_Exec(DN_Slice<DN_Str8> cmd_line,
|
||||
DN_OSExecArgs *args,
|
||||
DN_Arena *arena,
|
||||
DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSExecAsyncHandle async_handle = DN_OS_ExecAsync(cmd_line, args, error);
|
||||
DN_OSExecResult result = DN_OS_ExecWait(async_handle, arena, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena)
|
||||
{
|
||||
DN_OSErrSink *error = DN_OS_ErrSinkBegin(DN_OSErrSinkMode_Nil);
|
||||
DN_OSExecResult result = DN_OS_Exec(cmd_line, args, arena, error);
|
||||
if (result.os_error_code)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF(error, result.os_error_code, "OS failed to execute the requested command returning the error code %u", result.os_error_code);
|
||||
|
||||
if (result.exit_code)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF(error, result.exit_code, "OS executed command and returned non-zero exit code %u", result.exit_code);
|
||||
DN_OS_ErrSinkEndAndIgnore(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
||||
static void DN_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
DN_OSThread *thread = DN_CAST(DN_OSThread *) user_context;
|
||||
DN_OS_TLSInit(&thread->tls, thread->tls_init_args);
|
||||
DN_OS_TLSSetCurrentThreadTLS(&thread->tls);
|
||||
DN_OS_SemaphoreWait(&thread->init_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
tls->name_size = DN_CAST(uint8_t) DN_Min(name.size, sizeof(tls->name) - 1);
|
||||
DN_Memcpy(tls->name, name.data, tls->name_size);
|
||||
tls->name[tls->name_size] = 0;
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
DN_Win_ThreadSetName(name);
|
||||
#else
|
||||
DN_Posix_ThreadSetName(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
|
||||
{
|
||||
if (response && DN_OS_SemaphoreIsValid(&response->on_complete_semaphore))
|
||||
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers)
|
||||
{
|
||||
// TODO(doyle): Revise the memory allocation and its lifetime
|
||||
DN_OSHttpResponse result = {};
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
result.tmem_arena = tmem.arena;
|
||||
|
||||
DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
|
||||
DN_OS_HttpRequestWait(&result);
|
||||
return result;
|
||||
}
|
483
OS/dn_os.h
Normal file
483
OS/dn_os.h
Normal file
@ -0,0 +1,483 @@
|
||||
#if !defined(DN_OS_H)
|
||||
#define DN_OS_H
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_ARM64)
|
||||
#include "dn_os_posix.h"
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
#include "dn_os_windows.h"
|
||||
#include "dn_os_win32.h"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(DN_OS_WIN32) || defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDONLY ... etc
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/mman.h> // mmap
|
||||
#include <sys/random.h> // getrandom
|
||||
#include <sys/stat.h> // stat
|
||||
#include <sys/types.h> // pid_t
|
||||
#include <sys/wait.h> // waitpid
|
||||
#include <time.h> // clock_gettime, nanosleep
|
||||
#include <unistd.h> // access, gettid, write
|
||||
|
||||
#if !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <linux/fs.h> // FICLONE
|
||||
#include <sys/sendfile.h> // sendfile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
typedef HRESULT DN_WinSetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription);
|
||||
#endif
|
||||
|
||||
// NOTE: DN_OSDate /////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSDateTimeStr8
|
||||
{
|
||||
char date[DN_ArrayCountU("YYYY-MM-SS")];
|
||||
DN_U8 date_size;
|
||||
char hms[DN_ArrayCountU("HH:MM:SS")];
|
||||
DN_U8 hms_size;
|
||||
};
|
||||
|
||||
struct DN_OSDateTime
|
||||
{
|
||||
DN_U8 day;
|
||||
DN_U8 month;
|
||||
DN_U16 year;
|
||||
DN_U8 hour;
|
||||
DN_U8 minutes;
|
||||
DN_U8 seconds;
|
||||
};
|
||||
|
||||
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
|
||||
{
|
||||
DN_U64 start;
|
||||
DN_U64 end;
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: DN_OSFile /////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSPathInfoType
|
||||
{
|
||||
DN_OSPathInfoType_Unknown,
|
||||
DN_OSPathInfoType_Directory,
|
||||
DN_OSPathInfoType_File,
|
||||
};
|
||||
|
||||
struct DN_OSPathInfo
|
||||
{
|
||||
bool exists;
|
||||
DN_OSPathInfoType type;
|
||||
DN_U64 create_time_in_s;
|
||||
DN_U64 last_write_time_in_s;
|
||||
DN_U64 last_access_time_in_s;
|
||||
DN_U64 size;
|
||||
};
|
||||
|
||||
struct DN_OSDirIterator
|
||||
{
|
||||
void *handle;
|
||||
DN_Str8 file_name;
|
||||
char buffer[512];
|
||||
};
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSFileRead
|
||||
{
|
||||
bool success;
|
||||
DN_USize bytes_read;
|
||||
};
|
||||
|
||||
struct DN_OSFile
|
||||
{
|
||||
bool error;
|
||||
void *handle;
|
||||
};
|
||||
|
||||
enum DN_OSFileOpen
|
||||
{
|
||||
DN_OSFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
|
||||
DN_OSFileOpen_OpenIfExist, // Open file at path only if it exists
|
||||
DN_OSFileOpen_OpenAlways, // Open file at path, create file if it does not exist
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_OSFileAccess;
|
||||
|
||||
enum DN_OSFileAccess_
|
||||
{
|
||||
DN_OSFileAccess_Read = 1 << 0,
|
||||
DN_OSFileAccess_Write = 1 << 1,
|
||||
DN_OSFileAccess_Execute = 1 << 2,
|
||||
DN_OSFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
|
||||
DN_OSFileAccess_ReadWrite = DN_OSFileAccess_Read | DN_OSFileAccess_Write,
|
||||
DN_OSFileAccess_All = DN_OSFileAccess_ReadWrite | DN_OSFileAccess_Execute | DN_OSFileAccess_AppendOnly,
|
||||
};
|
||||
#endif // DN_NO_OS_FILE_API
|
||||
|
||||
// NOTE: DN_OSPath ////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_OSPathSeperator)
|
||||
#if defined(DN_OS_WIN32)
|
||||
#define DN_OSPathSeperator "\\"
|
||||
#else
|
||||
#define DN_OSPathSeperator "/"
|
||||
#endif
|
||||
#define DN_OSPathSeperatorString DN_STR8(DN_OSPathSeperator)
|
||||
#endif
|
||||
|
||||
struct DN_OSPathLink
|
||||
{
|
||||
DN_Str8 string;
|
||||
DN_OSPathLink *next;
|
||||
DN_OSPathLink *prev;
|
||||
};
|
||||
|
||||
struct DN_OSPath
|
||||
{
|
||||
bool has_prefix_path_separator;
|
||||
DN_OSPathLink *head;
|
||||
DN_OSPathLink *tail;
|
||||
DN_USize string_size;
|
||||
DN_U16 links_size;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
typedef DN_U32 DN_OSExecFlags;
|
||||
|
||||
enum DN_OSExecFlags_
|
||||
{
|
||||
DN_OSExecFlags_Nil = 0,
|
||||
DN_OSExecFlags_SaveStdout = 1 << 0,
|
||||
DN_OSExecFlags_SaveStderr = 1 << 1,
|
||||
DN_OSExecFlags_SaveOutput = DN_OSExecFlags_SaveStdout | DN_OSExecFlags_SaveStderr,
|
||||
DN_OSExecFlags_MergeStderrToStdout = 1 << 2 | DN_OSExecFlags_SaveOutput,
|
||||
};
|
||||
|
||||
struct DN_OSExecAsyncHandle
|
||||
{
|
||||
DN_OSExecFlags exec_flags;
|
||||
DN_U32 os_error_code;
|
||||
DN_U32 exit_code;
|
||||
void *process;
|
||||
void *stdout_read;
|
||||
void *stdout_write;
|
||||
void *stderr_read;
|
||||
void *stderr_write;
|
||||
};
|
||||
|
||||
struct DN_OSExecResult
|
||||
{
|
||||
bool finished;
|
||||
DN_Str8 stdout_text;
|
||||
DN_Str8 stderr_text;
|
||||
DN_U32 os_error_code;
|
||||
DN_U32 exit_code;
|
||||
};
|
||||
|
||||
struct DN_OSExecArgs
|
||||
{
|
||||
DN_OSExecFlags flags;
|
||||
DN_Str8 working_dir;
|
||||
DN_Slice<DN_Str8> environment;
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_SEMAPHORE)
|
||||
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
||||
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
|
||||
|
||||
struct DN_OSSemaphore
|
||||
{
|
||||
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
void *win32_handle;
|
||||
#else
|
||||
sem_t posix_handle;
|
||||
bool posix_init;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DN_OSSemaphoreWaitResult
|
||||
{
|
||||
DN_OSSemaphoreWaitResult_Failed,
|
||||
DN_OSSemaphoreWaitResult_Success,
|
||||
DN_OSSemaphoreWaitResult_Timeout,
|
||||
};
|
||||
#endif // !defined(DN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSMutex
|
||||
{
|
||||
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
char win32_handle[48];
|
||||
#else
|
||||
pthread_mutex_t posix_handle;
|
||||
pthread_mutexattr_t posix_attribs;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
|
||||
typedef int32_t(DN_OSThreadFunc)(struct DN_OSThread *);
|
||||
|
||||
struct DN_OSThread
|
||||
{
|
||||
DN_FStr8<64> name;
|
||||
DN_OSTLS tls;
|
||||
DN_OSTLSInitArgs tls_init_args;
|
||||
void *handle;
|
||||
DN_U64 thread_id;
|
||||
void *user_context;
|
||||
DN_OSThreadFunc *func;
|
||||
DN_OSSemaphore init_semaphore;
|
||||
};
|
||||
#endif // !defined(DN_NO_THREAD)
|
||||
|
||||
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSHttpRequestSecure
|
||||
{
|
||||
DN_OSHttpRequestSecure_No,
|
||||
DN_OSHttpRequestSecure_Yes,
|
||||
};
|
||||
|
||||
struct DN_OSHttpResponse
|
||||
{
|
||||
// NOTE: Response data
|
||||
DN_U32 error_code;
|
||||
DN_Str8 error_msg;
|
||||
DN_U16 http_status;
|
||||
DN_Str8 body;
|
||||
DN_B32 done;
|
||||
|
||||
// NOTE: Book-keeping
|
||||
DN_Arena *arena; // Allocates memory for the response
|
||||
|
||||
// NOTE: Async book-keeping
|
||||
// Synchronous HTTP response uses the TLS scratch arena whereas async
|
||||
// calls use their own dedicated arena.
|
||||
DN_Arena tmp_arena;
|
||||
DN_Arena *tmem_arena;
|
||||
DN_Str8Builder builder;
|
||||
DN_OSSemaphore on_complete_semaphore;
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_t *em_handle;
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
HINTERNET win32_request_session;
|
||||
HINTERNET win32_request_connection;
|
||||
HINTERNET win32_request_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DN_OSInitArgs
|
||||
{
|
||||
DN_U64 tls_reserve;
|
||||
DN_U64 tls_commit;
|
||||
DN_U64 tls_err_sink_reserve;
|
||||
DN_U64 tls_err_sink_commit;
|
||||
};
|
||||
|
||||
struct DN_OSCore
|
||||
{
|
||||
DN_CPUReport cpu_report;
|
||||
DN_OSTLS tls; // Thread local storage state for the main thread.
|
||||
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_LOGEmitFromTypeFVFunc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data; // User pointer passed into 'log_callback'
|
||||
bool log_to_file; // Output logs to file as well as standard out
|
||||
DN_OSFile log_file; // TODO(dn): Hmmm, how should we do this... ?
|
||||
DN_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
|
||||
|
||||
// NOTE: OS //////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_U32 page_size;
|
||||
DN_U32 alloc_granularity;
|
||||
|
||||
// NOTE: Memory ////////////////////////////////////////////////////////////////////////////////
|
||||
// Total OS mem allocs in lifetime of program (e.g. malloc, VirtualAlloc, HeapAlloc ...). This
|
||||
// only includes allocations routed through the library such as the growing nature of arenas or
|
||||
// using the memory allocation routines in the library like DN_OS_MemCommit and so forth.
|
||||
DN_U64 vmem_allocs_total;
|
||||
DN_U64 vmem_allocs_frame; // Total OS virtual memory allocs since the last 'DN_Core_FrameBegin' was invoked
|
||||
DN_U64 mem_allocs_total;
|
||||
DN_U64 mem_allocs_frame; // Total OS heap allocs since the last 'DN_Core_FrameBegin' was invoked
|
||||
|
||||
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
DN_WinSetThreadDescriptionFunc *win32_set_thread_description;
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
void * win32_bcrypt_rng_handle;
|
||||
bool win32_bcrypt_init_success;
|
||||
bool win32_sym_initialised;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DN_OSDiskSpace
|
||||
{
|
||||
bool success;
|
||||
DN_U64 avail;
|
||||
DN_U64 size;
|
||||
};
|
||||
|
||||
DN_API void DN_OS_Init (DN_OSCore *os, DN_OSInitArgs *args);
|
||||
DN_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os);
|
||||
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
||||
|
||||
// NOTE: Memory ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void * DN_OS_MemReserve (DN_USize size, DN_MemCommit commit, DN_MemPage page_flags);
|
||||
DN_API bool DN_OS_MemCommit (void *ptr, DN_USize size, DN_U32 page_flags);
|
||||
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size);
|
||||
DN_API void DN_OS_MemRelease (void *ptr, DN_USize size);
|
||||
DN_API int DN_OS_MemProtect (void *ptr, DN_USize size, DN_U32 page_flags);
|
||||
|
||||
// NOTE: Heap
|
||||
DN_API void * DN_OS_MemAlloc (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API void DN_OS_MemDealloc (void *ptr);
|
||||
|
||||
// NOTE: DN_OSDate /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTime DN_OS_DateLocalTimeNow ();
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8 (DN_OSDateTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DN_API DN_U64 DN_OS_DateUnixTimeNs ();
|
||||
DN_API DN_U64 DN_OS_DateUnixTimeS ();
|
||||
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate (DN_U64 time);
|
||||
DN_API DN_U64 DN_OS_DateLocalToUnixTimeS(DN_OSDateTime date);
|
||||
DN_API DN_U64 DN_OS_DateToUnixTimeS (DN_OSDateTime date);
|
||||
DN_API bool DN_OS_DateIsValid (DN_OSDateTime date);
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_SecureRNGBytes (void *buffer, DN_U32 size);
|
||||
DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value);
|
||||
DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path);
|
||||
DN_API DN_Str8 DN_OS_EXEPath (DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_OS_EXEDir (DN_Arena *arena);
|
||||
#define DN_OS_EXEDirFromTLS() DN_OS_EXEDir(DN_OS_TLSTopArena())
|
||||
DN_API void DN_OS_SleepMs (DN_UInt milliseconds);
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_U64 DN_OS_PerfCounterNow ();
|
||||
DN_API DN_U64 DN_OS_PerfCounterFrequency();
|
||||
DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin ();
|
||||
DN_API void DN_OS_TimerEnd (DN_OSTimer *timer);
|
||||
DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer);
|
||||
DN_API DN_U64 DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSPathInfo DN_OS_PathInfo (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against);
|
||||
DN_API bool DN_OS_PathDelete (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_CopyFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_MoveFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_MakeDir (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirIterate (DN_Str8 path, DN_OSDirIterator *it);
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSFile DN_OS_FileOpen (DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_OSErrSink *err);
|
||||
DN_API DN_OSFileRead DN_OS_FileRead (DN_OSFile *file, void *buffer, DN_USize size, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *data, DN_USize size, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWrite (DN_OSFile *file, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWriteFV (DN_OSFile *file, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_FileWriteF (DN_OSFile *file, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_FileFlush (DN_OSFile *file, DN_OSErrSink *err);
|
||||
DN_API void DN_OS_FileClose (DN_OSFile *file);
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll (DN_Arena *arena, DN_Str8 path, DN_OSErrSink *err);
|
||||
#define DN_OS_ReadAllFromTLS(...) DN_OS_ReadAll(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_WriteAll (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_WriteAllSafe (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllSafeFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllSafeF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAddRefFromTLS(...) DN_OS_PathAddRef(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddRefFromFrame(...) DN_OS_PathAddRef(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAdd (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAddFromTLS(...) DN_OS_PathAdd(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddFromFrame(...) DN_OS_PathAdd(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAddF (DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathAddFFromTLS(...) DN_OS_PathAddF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddFFromFrame(...) DN_OS_PathAddF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathPop (DN_OSPath *fs_path);
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator (DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator);
|
||||
#define DN_OS_PathBuildWithSeperatorFromTLS(...) DN_OS_PathBuildWithSeperator(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathBuildWithSeperatorFromFrame(...) DN_OS_PathBuildWithSeperator(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathTo (DN_Arena *arena, DN_Str8 path, DN_Str8 path_separtor);
|
||||
#define DN_OS_PathToFromTLS(...) DN_OS_PathTo(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathToFromFrame(...) DN_OS_PathTo(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathToF (DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathToFFromTLS(...) DN_OS_PathToF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathToFFromFrame(...) DN_OS_PathToF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_Path (DN_Arena *arena, DN_Str8 path);
|
||||
#define DN_OS_PathFromTLS(...) DN_OS_Path(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathFromFrame(...) DN_OS_Path(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathFFromTLS(...) DN_OS_PathF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathFFromFrame(...) DN_OS_PathF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
|
||||
#define DN_OS_PathBuildFwdSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("/"))
|
||||
#define DN_OS_PathBuildBackSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("\\"))
|
||||
#define DN_OS_PathBuild(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_OSPathSeparatorString)
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_Exit (int32_t exit_code);
|
||||
DN_API DN_OSExecResult DN_OS_ExecPump (DN_OSExecAsyncHandle handle, char *stdout_buffer, size_t *stdout_size, char *stderr_buffer, size_t *stderr_size, DN_U32 timeout_ms, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecWait (DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_Exec (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena);
|
||||
#define DN_OS_ExecOrAbortFromTLS(...) DN_OS_ExecOrAbort(__VA_ARGS__, DN_OS_TLSTopArena())
|
||||
|
||||
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_SEMAPHORE)
|
||||
DN_API DN_OSSemaphore DN_OS_SemaphoreInit (DN_U32 initial_count);
|
||||
DN_API bool DN_OS_SemaphoreIsValid (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreDeinit (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount);
|
||||
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait (DN_OSSemaphore *semaphore, DN_U32 timeout_ms);
|
||||
#endif // !defined(DN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSMutex DN_OS_MutexInit ();
|
||||
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexLock (DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex);
|
||||
#define DN_OS_Mutex(mutex) DN_DEFER_LOOP(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
|
||||
|
||||
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
|
||||
DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context);
|
||||
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread);
|
||||
DN_API DN_U32 DN_OS_ThreadID ();
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name);
|
||||
#endif // !defined(DN_NO_THREAD)
|
||||
|
||||
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
||||
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
|
||||
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
||||
#endif // !defined(DN_OS_H)
|
30
OS/dn_os_allocator.cpp
Normal file
30
OS/dn_os_allocator.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#define DN_OS_ALLOCATOR_CPP
|
||||
|
||||
static void *DN_Arena_BasicAllocFromOSHeap(DN_USize size)
|
||||
{
|
||||
void *result = DN_OS_MemAlloc(size, DN_ZeroMem_Yes);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSHeap(DN_U64 size, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_ArenaMemFuncs mem_funcs = {};
|
||||
mem_funcs.type = DN_ArenaMemFuncType_Basic;
|
||||
mem_funcs.basic_alloc = DN_Arena_BasicAllocFromOSHeap;
|
||||
mem_funcs.basic_dealloc = DN_OS_MemDealloc;
|
||||
DN_Arena result = DN_Arena_InitFromMemFuncs(size, size, flags, mem_funcs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_ArenaMemFuncs mem_funcs = {};
|
||||
mem_funcs.type = DN_ArenaMemFuncType_VMem;
|
||||
mem_funcs.vmem_page_size = g_dn_os_core_->page_size;
|
||||
mem_funcs.vmem_reserve = DN_OS_MemReserve;
|
||||
mem_funcs.vmem_commit = DN_OS_MemCommit;
|
||||
mem_funcs.vmem_release = DN_OS_MemRelease;
|
||||
DN_Arena result = DN_Arena_InitFromMemFuncs(reserve, commit, flags, mem_funcs);
|
||||
return result;
|
||||
}
|
||||
|
7
OS/dn_os_allocator.h
Normal file
7
OS/dn_os_allocator.h
Normal file
@ -0,0 +1,7 @@
|
||||
#if !defined(DN_OS_ALLOCATOR_H)
|
||||
#define DN_OS_ALLOCATOR_H
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSHeap(DN_U64 size, DN_ArenaFlags flags);
|
||||
DN_API DN_Arena DN_Arena_InitFromOSVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags);
|
||||
|
||||
#endif // !defined(DN_OS_ALLOCATOR_H)
|
202
OS/dn_os_containers.cpp
Normal file
202
OS/dn_os_containers.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#define DN_OS_CONTAINERS_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ / $$ | $$ | $$$$\ $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$$$$ | $$ | $$ $$\$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __$$ | $$ | $$ \$$$$ |$$ __| $$ __$$< \____$$\
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |$$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \______/ \______/ \__| \__| \__| \__| \__|\______|\__| \__|\________|\__| \__| \______/
|
||||
//
|
||||
// dn_containers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: DN_VArray /////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_InitByteSize(DN_USize byte_size)
|
||||
{
|
||||
DN_VArray<T> result = {};
|
||||
result.data = DN_CAST(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite);
|
||||
if (result.data)
|
||||
result.max = byte_size / sizeof(T);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_Init(DN_USize max)
|
||||
{
|
||||
DN_VArray<T> result = DN_VArray_InitByteSize<T>(max * sizeof(T));
|
||||
DN_Assert(result.max >= max);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_InitSlice(DN_Slice<T> slice, DN_USize max)
|
||||
{
|
||||
DN_USize real_max = DN_Max(slice.size, max);
|
||||
DN_VArray<T> result = DN_VArray_Init<T>(real_max);
|
||||
if (DN_VArray_IsValid(&result))
|
||||
DN_VArray_AddArray(&result, slice.data, slice.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
DN_VArray<T> DN_VArray_InitCArray(T const (&items)[N], DN_USize max)
|
||||
{
|
||||
DN_USize real_max = DN_Max(N, max);
|
||||
DN_VArray<T> result = DN_VArray_InitSlice(DN_Slice_Init(items, N), real_max);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_VArray_Deinit(DN_VArray<T> *array)
|
||||
{
|
||||
DN_OS_MemRelease(array->data, array->max * sizeof(T));
|
||||
*array = {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_VArray_IsValid(DN_VArray<T> const *array)
|
||||
{
|
||||
bool result = array->data && array->size <= array->max;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_Slice<T> DN_VArray_Slice(DN_VArray<T> const *array)
|
||||
{
|
||||
DN_Slice<T> result = {};
|
||||
if (array)
|
||||
result = DN_Slice_Init<T>(array->data, array->size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_AddArray(DN_VArray<T> *array, T const *items, DN_USize count)
|
||||
{
|
||||
T *result = DN_VArray_MakeArray(array, count, DN_ZeroMem_No);
|
||||
if (result)
|
||||
DN_Memcpy(result, items, count * sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
T *DN_VArray_AddCArray(DN_VArray<T> *array, T const (&items)[N])
|
||||
{
|
||||
T *result = DN_VArray_AddArray(array, items, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Add(DN_VArray<T> *array, T const &item)
|
||||
{
|
||||
T *result = DN_VArray_AddArray(array, &item, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_MakeArray(DN_VArray<T> *array, DN_USize count, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return nullptr;
|
||||
|
||||
if (!DN_CheckF((array->size + count) < array->max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, array->size, array->max))
|
||||
return nullptr;
|
||||
|
||||
if (!DN_VArray_Reserve(array, count))
|
||||
return nullptr;
|
||||
|
||||
// TODO: Use placement new
|
||||
T *result = array->data + array->size;
|
||||
array->size += count;
|
||||
if (zero_mem == DN_ZeroMem_Yes)
|
||||
DN_Memset(result, 0, count * sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Make(DN_VArray<T> *array, DN_ZeroMem zero_mem)
|
||||
{
|
||||
T *result = DN_VArray_MakeArray(array, 1, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_InsertArray(DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count)
|
||||
{
|
||||
T *result = nullptr;
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return result;
|
||||
if (DN_VArray_Reserve(array, array->size + count))
|
||||
result = DN_CArray_InsertArray(array->data, &array->size, array->max, index, items, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
T *DN_VArray_InsertCArray(DN_VArray<T> *array, DN_USize index, T const (&items)[N])
|
||||
{
|
||||
T *result = DN_VArray_InsertArray(array, index, items, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Insert(DN_VArray<T> *array, DN_USize index, T const &item)
|
||||
{
|
||||
T *result = DN_VArray_InsertArray(array, index, &item, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_PopFront(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
T *result = DN_CArray_PopFront(array->data, &array->size, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_PopBack(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
T *result = DN_CArray_PopBack(array->data, &array->size, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_ArrayEraseResult DN_VArray_EraseRange(DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
||||
{
|
||||
DN_ArrayEraseResult result = {};
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return result;
|
||||
result = DN_CArray_EraseRange<T>(array->data, &array->size, begin_index, count, erase);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_VArray_Clear(DN_VArray<T> *array, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (array) {
|
||||
if (zero_mem == DN_ZeroMem_Yes)
|
||||
DN_Memset(array->data, 0, array->size * sizeof(T));
|
||||
array->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_VArray_Reserve(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
if (!DN_VArray_IsValid(array) || count == 0)
|
||||
return false;
|
||||
|
||||
DN_USize real_commit = (array->size + count) * sizeof(T);
|
||||
DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, g_dn_os_core_->page_size);
|
||||
if (array->commit >= aligned_commit)
|
||||
return true;
|
||||
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);
|
||||
array->commit = aligned_commit;
|
||||
return result;
|
||||
}
|
47
OS/dn_os_containers.h
Normal file
47
OS/dn_os_containers.h
Normal file
@ -0,0 +1,47 @@
|
||||
#if !defined(DN_OS_CONTAINERS_H)
|
||||
#define DN_OS_CONTAINERS_H
|
||||
|
||||
// NOTE: DN_VArray /////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS.
|
||||
template <typename T> struct DN_VArray
|
||||
{
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
DN_USize max; // Maximum number of items this array can store
|
||||
DN_USize commit; // Bytes committed
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitByteSize (DN_USize byte_size);
|
||||
template <typename T> DN_VArray<T> DN_VArray_Init (DN_USize max);
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitSlice (DN_Slice<T> slice, DN_USize max);
|
||||
template <typename T, DN_USize N> DN_VArray<T> DN_VArray_InitCArray (T const (&items)[N], DN_USize max);
|
||||
template <typename T> void DN_VArray_Deinit (DN_VArray<T> *array);
|
||||
template <typename T> bool DN_VArray_IsValid (DN_VArray<T> const *array);
|
||||
template <typename T> DN_Slice<T> DN_VArray_Slice (DN_VArray<T> const *array);
|
||||
template <typename T> bool DN_VArray_Reserve (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T * DN_VArray_AddArray (DN_VArray<T> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_AddCArray (DN_VArray<T> *array, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Add (DN_VArray<T> *array, T const &item);
|
||||
#define DN_VArray_AddArrayAssert(...) DN_HardAssert(DN_VArray_AddArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddCArrayAssert(...) DN_HardAssert(DN_VArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddAssert(...) DN_HardAssert(DN_VArray_Add(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_MakeArray (DN_VArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_VArray_Make (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_VArray_MakeArrayAssert(...) DN_HardAssert(DN_VArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_VArray_MakeAssert(...) DN_HardAssert(DN_VArray_Make(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_InsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_InsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Insert (DN_VArray<T> *array, DN_USize index, T const &item);
|
||||
#define DN_VArray_InsertArrayAssert(...) DN_HardAssert(DN_VArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertCArrayAssert(...) DN_HardAssert(DN_VArray_InsertCArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertAssert(...) DN_HardAssert(DN_VArray_Insert(__VA_ARGS__))
|
||||
template <typename T> T DN_VArray_PopFront (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T DN_VArray_PopBack (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> DN_ArrayEraseResult DN_VArray_EraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> void DN_VArray_Clear (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#endif // !defined(DN_OS_CONTAINERS_H)
|
1415
OS/dn_os_posix.cpp
Normal file
1415
OS/dn_os_posix.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,9 +12,19 @@
|
||||
// $$$$$$ |\$$$$$$ | $$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ / $$ |
|
||||
// \______/ \______/ \__| \______/ \______/ \______|\__| \__|
|
||||
//
|
||||
// dqn_os_posix.h
|
||||
// dn_os_posix.h
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
DN_API void DN_Posix_ThreadSetName(DN_Str8 name);
|
||||
struct DN_POSIXProcSelfStatus
|
||||
{
|
||||
char name[64];
|
||||
DN_U8 name_size;
|
||||
DN_U32 pid;
|
||||
DN_U64 vm_peak;
|
||||
DN_U32 vm_size;
|
||||
};
|
||||
|
||||
DN_API void DN_Posix_ThreadSetName(DN_Str8 name);
|
||||
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus();
|
170
OS/dn_os_print.cpp
Normal file
170
OS/dn_os_print.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#define DN_OS_PRINT_CPP
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LOGBold bold)
|
||||
{
|
||||
DN_LOGStyle result = {};
|
||||
result.bold = bold;
|
||||
result.colour = true;
|
||||
result.r = r;
|
||||
result.g = g;
|
||||
result.b = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleColourU32(uint32_t rgb, DN_LOGBold bold)
|
||||
{
|
||||
uint8_t r = (rgb >> 24) & 0xFF;
|
||||
uint8_t g = (rgb >> 16) & 0xFF;
|
||||
uint8_t b = (rgb >> 8) & 0xFF;
|
||||
DN_LOGStyle result = DN_OS_PrintStyleColour(r, g, b, bold);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleBold()
|
||||
{
|
||||
DN_LOGStyle result = {};
|
||||
result.bold = DN_LOGBold_Yes;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_Print(DN_OSPrintDest dest, DN_Str8 string)
|
||||
{
|
||||
DN_Assert(dest == DN_OSPrintDest_Out || dest == DN_OSPrintDest_Err);
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
// NOTE: Get the output handles from kernel ////////////////////////////////////////////////////
|
||||
DN_THREAD_LOCAL void *std_out_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL void *std_err_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL bool std_out_print_to_console = false;
|
||||
DN_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 (dest == DN_OSPrintDest_Err) {
|
||||
print_handle = std_err_print_handle;
|
||||
print_to_console = std_err_print_to_console;
|
||||
}
|
||||
|
||||
// NOTE: Write the string //////////////////////////////////////////////////////////////////////
|
||||
DN_Assert(string.size < DN_CAST(unsigned long) - 1);
|
||||
unsigned long bytes_written = 0;
|
||||
(void)bytes_written;
|
||||
if (print_to_console)
|
||||
WriteConsoleA(print_handle, string.data, DN_CAST(unsigned long) string.size, &bytes_written, nullptr);
|
||||
else
|
||||
WriteFile(print_handle, string.data, DN_CAST(unsigned long) string.size, &bytes_written, nullptr);
|
||||
#else
|
||||
fprintf(dest == DN_OSPrintDest_Out ? stdout : stderr, "%.*s", DN_STR_FMT(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintF(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintFVStyle(dest, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string)
|
||||
{
|
||||
if (string.data && string.size) {
|
||||
if (style.colour)
|
||||
DN_OS_Print(dest, DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b));
|
||||
if (style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_BoldEscapeCode));
|
||||
DN_OS_Print(dest, string);
|
||||
if (style.colour || style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_ResetEscapeCode));
|
||||
}
|
||||
}
|
||||
|
||||
static char *DN_OS_PrintVSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
DN_Str8 string = {};
|
||||
string.data = DN_CAST(char *) buf;
|
||||
string.size = len;
|
||||
|
||||
DN_OSPrintDest dest = DN_CAST(DN_OSPrintDest) DN_CAST(uintptr_t) user;
|
||||
DN_OS_Print(dest, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFV(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)
|
||||
(DN_OS_PrintVSPrintfChunker_, DN_CAST(void *) DN_CAST(uintptr_t) dest, buffer, fmt, args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFVStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (fmt) {
|
||||
if (style.colour)
|
||||
DN_OS_Print(dest, DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b));
|
||||
if (style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_BoldEscapeCode));
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
if (style.colour || style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_ResetEscapeCode));
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLn(DN_OSPrintDest dest, DN_Str8 string)
|
||||
{
|
||||
DN_OS_Print(dest, string);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnF(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintLnFV(dest, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFV(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string)
|
||||
{
|
||||
DN_OS_PrintStyle(dest, style, string);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintLnFVStyle(dest, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OS_PrintFVStyle(dest, style, fmt, args);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
59
OS/dn_os_print.h
Normal file
59
OS/dn_os_print.h
Normal file
@ -0,0 +1,59 @@
|
||||
#if !defined(DN_OS_PRINT_H)
|
||||
#define DN_OS_PRINT_H
|
||||
|
||||
enum DN_OSPrintDest
|
||||
{
|
||||
DN_OSPrintDest_Out,
|
||||
DN_OSPrintDest_Err,
|
||||
};
|
||||
|
||||
// NOTE: Print Macros //////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_OS_PrintOut(string) DN_OS_Print(DN_OSPrintDest_Out, string)
|
||||
#define DN_OS_PrintOutF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Out, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Out, style, string)
|
||||
#define DN_OS_PrintOutFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Out, style, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutLn(string) DN_OS_PrintLn(DN_OSPrintDest_Out, string)
|
||||
#define DN_OS_PrintOutLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Out, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Out, style, string);
|
||||
#define DN_OS_PrintOutLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Out, style, fmt, args);
|
||||
|
||||
#define DN_OS_PrintErr(string) DN_OS_Print(DN_OSPrintDest_Err, string)
|
||||
#define DN_OS_PrintErrF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Err, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Err, style, string)
|
||||
#define DN_OS_PrintErrFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Err, style, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrLn(string) DN_OS_PrintLn(DN_OSPrintDest_Err, string)
|
||||
#define DN_OS_PrintErrLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Err, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Err, style, string);
|
||||
#define DN_OS_PrintErrLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Err, style, fmt, args);
|
||||
|
||||
// NOTE: Print /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_Print (DN_OSPrintDest dest, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintFStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintFVStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintLn (DN_OSPrintDest dest, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintLnF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
#endif // !defined(DN_OS_PRINT_H)
|
304
OS/dn_os_string.cpp
Normal file
304
OS/dn_os_string.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#define DN_OS_STRING_CPP
|
||||
|
||||
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromFrame(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSGet()->frame_arena, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromOSHeap(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
DN_Str8 result = {};
|
||||
DN_USize size = DN_CStr8_FVSize(fmt, args);
|
||||
if (size) {
|
||||
result = DN_Str8_AllocFromOSHeap(size, DN_ZeroMem_No);
|
||||
if (DN_Str8_HasData(result))
|
||||
DN_VSNPrintF(result.data, DN_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromTLS(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSTopArena(), fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromFrame(DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSGet()->frame_arena, fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromTLS(DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSTopArena(), fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromFrame(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Alloc(DN_OS_TLSGet()->frame_arena, size, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromOSHeap(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
result.data = DN_CAST(char *)DN_OS_MemAlloc(size + 1, zero_mem);
|
||||
if (result.data)
|
||||
result.size = size;
|
||||
result.data[result.size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromTLS(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Alloc(DN_OS_TLSTopArena(), size, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Copy(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Copy(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromFrame(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = DN_Str8_SplitAlloc(DN_OS_TLSGet()->frame_arena, string, delimiter, mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromTLS(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = DN_Str8_SplitAlloc(DN_OS_TLSTopArena(), string, delimiter, mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Segment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Segment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_ReverseSegment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_ReverseSegment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromFrame(DN_Str8 string, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_AppendFV(DN_OS_TLSGet()->frame_arena, string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromTLS(DN_Str8 string, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_AppendFV(DN_OS_TLSTopArena(), string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromFrame(DN_USize count, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_FillFV(DN_OS_TLSGet()->frame_arena, count, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromTLS(DN_USize count, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_FillFV(DN_OS_TLSTopArena(), count, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromFrame(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
||||
{
|
||||
DN_Str8DotTruncateResult result = DN_Str8_DotTruncateMiddle(DN_OS_TLSGet()->frame_arena, str8, side_size, truncator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromTLS(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
||||
{
|
||||
DN_Str8DotTruncateResult result = DN_Str8_DotTruncateMiddle(DN_OS_TLSTopArena(), str8, side_size, truncator);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
// TODO: Implement this without requiring TLS so it can go into base strings
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
|
||||
DN_Str8BinarySplitResult split = DN_Str8_BinarySplit(src, DN_STR8("\n"));
|
||||
while (split.lhs.size) {
|
||||
DN_Str8Builder_AppendRef(&builder, pad);
|
||||
DN_Str8Builder_AppendRef(&builder, split.lhs);
|
||||
split = DN_Str8_BinarySplit(split.rhs, DN_STR8("\n"));
|
||||
if (split.lhs.size)
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_Str8 result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromFrame(DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_PadNewLines(DN_OS_TLSGet()->frame_arena, src, pad);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromTLS(DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_PadNewLines(DN_OS_TLSTopArena(), src, pad);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Upper(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Upper(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Lower(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Lower(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_Replace(DN_Str8 string,
|
||||
DN_Str8 find,
|
||||
DN_Str8 replace,
|
||||
DN_USize start_index,
|
||||
DN_Arena *arena,
|
||||
DN_Str8EqCase eq_case)
|
||||
{
|
||||
// TODO: Implement this without requiring TLS so it can go into base strings
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size || find.size == 0 || string.size == 0) {
|
||||
result = DN_Str8_Copy(arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_Str8Builder string_builder = DN_Str8Builder_Init(tmem.arena);
|
||||
DN_USize max = string.size - find.size;
|
||||
DN_USize head = start_index;
|
||||
|
||||
for (DN_USize tail = head; tail <= max; tail++) {
|
||||
DN_Str8 check = DN_Str8_Slice(string, tail, find.size);
|
||||
if (!DN_Str8_Eq(check, find, eq_case))
|
||||
continue;
|
||||
|
||||
if (start_index > 0 && string_builder.string_size == 0) {
|
||||
// User provided a hint in the string to start searching from, we
|
||||
// need to add the string up to the hint. We only do this if there's
|
||||
// a replacement action, otherwise we have a special case for no
|
||||
// replacements, where the entire string gets copied.
|
||||
DN_Str8 slice = DN_Str8_Init(string.data, head);
|
||||
DN_Str8Builder_AppendRef(&string_builder, slice);
|
||||
}
|
||||
|
||||
DN_Str8 range = DN_Str8_Slice(string, head, (tail - head));
|
||||
DN_Str8Builder_AppendRef(&string_builder, range);
|
||||
DN_Str8Builder_AppendRef(&string_builder, replace);
|
||||
head = tail + find.size;
|
||||
tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string
|
||||
}
|
||||
|
||||
if (string_builder.string_size == 0) {
|
||||
// NOTE: No replacement possible, so we just do a full-copy
|
||||
result = DN_Str8_Copy(arena, string);
|
||||
} else {
|
||||
DN_Str8 remainder = DN_Str8_Init(string.data + head, string.size - head);
|
||||
DN_Str8Builder_AppendRef(&string_builder, remainder);
|
||||
result = DN_Str8Builder_Build(&string_builder, arena);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReplaceInsensitive(DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Replace(string, find, replace, start_index, arena, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromOSHeap(DN_Str8Builder const *builder)
|
||||
{
|
||||
DN_Str8 result = DN_ZeroInit;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DN_CAST(char *) DN_OS_MemAlloc(builder->string_size + 1, DN_ZeroMem_No);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
for (DN_Str8Link *link = builder->head; link; link = link->next) {
|
||||
DN_Memcpy(result.data + result.size, link->string.data, link->string.size);
|
||||
result.size += link->string.size;
|
||||
}
|
||||
|
||||
result.data[result.size] = 0;
|
||||
DN_Assert(result.size == builder->string_size);
|
||||
return result;
|
||||
}
|
74
OS/dn_os_string.h
Normal file
74
OS/dn_os_string.h
Normal file
@ -0,0 +1,74 @@
|
||||
#if !defined(DN_OS_STRING_H)
|
||||
#define DN_OS_STRING_H
|
||||
|
||||
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromFrame (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_InitFFromOSHeap (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_InitFFromTLS (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromFrame (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromTLS (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromFrame (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API DN_Str8 DN_Str8_AllocFromOSHeap (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API DN_Str8 DN_Str8_AllocFromTLS (DN_USize size, DN_ZeroMem zero_mem);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromFrame (DN_Arena *arena, DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_CopyFromTLS (DN_Arena *arena, DN_Str8 string);
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromFrame (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromTLS (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromFrame (DN_Str8 string, char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromTLS (DN_Str8 string, char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromFrame (DN_Str8 string, char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_FillFFromTLS (DN_Str8 string, char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromFrame (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromTLS (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLines (DN_Arena *arena, DN_Str8 src, DN_Str8 pad);
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromFrame (DN_Str8 src, DN_Str8 pad);
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromTLS (DN_Str8 src, DN_Str8 pad);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromFrame (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_UpperFromTLS (DN_Str8 string);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromFrame (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_LowerFromTLS (DN_Str8 string);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API DN_Str8 DN_Str8_ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
|
||||
|
||||
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitFromFrame () { return DN_Str8Builder_Init(DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitFromTLS () { return DN_Str8Builder_Init(DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRefFromFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayRef(DN_OS_TLSGet()->frame_arena, strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRefFromTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayRef(DN_OS_TLSTopArena(), strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopyFromFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayCopy(DN_OS_TLSGet()->frame_arena, strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopyFromTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayCopy(DN_OS_TLSTopArena(), strings, size); }
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_CopyFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_Copy(DN_OS_TLSGet()->frame_arena, builder); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_CopyFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_Copy(DN_OS_TLSTopArena(), builder); }
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_Build(builder, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildFromOSHeap (DN_Str8Builder const *builder, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_Build(builder, DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildDelimitedFromFrame(DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8Builder_BuildDelimited(builder, delimiter, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildDelimitedFromTLS (DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8Builder_BuildDelimited(builder, delimiter, DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSliceFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_BuildSlice(builder, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSliceFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_BuildSlice(builder, DN_OS_TLSTopArena()); }
|
||||
|
||||
#endif // !defined(DN_OS_STRING_H)
|
399
OS/dn_os_tls.cpp
Normal file
399
OS/dn_os_tls.cpp
Normal file
@ -0,0 +1,399 @@
|
||||
#define DN_OS_TLSCPP
|
||||
|
||||
// NOTE: DN_OSTLS ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_OSTLSTMem::DN_OSTLSTMem(DN_OSTLS *tls, DN_U8 arena_index, DN_OSTLSPushTMem push_tmem)
|
||||
{
|
||||
DN_Assert(arena_index == DN_OSTLSArena_TMem0 || arena_index == DN_OSTLSArena_TMem1);
|
||||
arena = tls->arenas + arena_index;
|
||||
temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
push_arena = push_tmem;
|
||||
if (push_arena)
|
||||
DN_OS_TLSPushArena(arena);
|
||||
}
|
||||
|
||||
DN_OSTLSTMem::~DN_OSTLSTMem()
|
||||
{
|
||||
DN_Assert(destructed == false);
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
if (push_arena)
|
||||
DN_OS_TLSPopArena();
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSInit(DN_OSTLS *tls, DN_OSTLSInitArgs args)
|
||||
{
|
||||
DN_Check(tls);
|
||||
if (tls->init)
|
||||
return;
|
||||
|
||||
DN_U64 reserve = args.reserve ? args.reserve : DN_Kilobytes(64);
|
||||
DN_U64 commit = args.commit ? args.commit : DN_Kilobytes(4);
|
||||
DN_U64 err_sink_reserve = args.err_sink_reserve ? args.err_sink_reserve : DN_Kilobytes(64);
|
||||
DN_U64 err_sink_commit = args.err_sink_commit ? args.err_sink_commit : DN_Kilobytes(4);
|
||||
|
||||
// TODO: We shouldn't have the no alloc track flag here but the initial TLS
|
||||
// init on OS init happens before CORE init. CORE init is the one responsible
|
||||
// for setting up the alloc tracking data structures.
|
||||
DN_ForIndexU(index, DN_OSTLSArena_Count) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
switch (DN_CAST(DN_OSTLSArena) index) {
|
||||
default: *arena = DN_Arena_InitFromOSVMem(reserve, commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
|
||||
case DN_OSTLSArena_ErrorSink: *arena = DN_Arena_InitFromOSVMem(err_sink_reserve, err_sink_commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
|
||||
case DN_OSTLSArena_Count: DN_InvalidCodePath; break;
|
||||
}
|
||||
}
|
||||
|
||||
tls->thread_id = DN_OS_ThreadID();
|
||||
tls->err_sink.arena = tls->arenas + DN_OSTLSArena_ErrorSink;
|
||||
tls->init = true;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSDeinit(DN_OSTLS *tls)
|
||||
{
|
||||
tls->init = false;
|
||||
tls->err_sink = {};
|
||||
tls->arena_stack_index = {};
|
||||
DN_ForIndexU(index, DN_OSTLSArena_Count) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
DN_Arena_Deinit(arena);
|
||||
}
|
||||
}
|
||||
|
||||
DN_THREAD_LOCAL DN_OSTLS *g_dn_curr_thread_tls;
|
||||
DN_API void DN_OS_TLSSetCurrentThreadTLS(DN_OSTLS *tls)
|
||||
{
|
||||
g_dn_curr_thread_tls = tls;
|
||||
}
|
||||
|
||||
DN_API DN_OSTLS *DN_OS_TLSGet()
|
||||
{
|
||||
DN_Assert(g_dn_curr_thread_tls &&
|
||||
"DN must be initialised (via DN_Core_Init) before calling any functions depending on "
|
||||
"TLS if this is the main thread, OR, the created thread has not called "
|
||||
"SetCurrentThreadTLS yet so the TLS data structure hasn't been assigned yet");
|
||||
return g_dn_curr_thread_tls;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = tls->arenas + DN_OSTLSArena_Main;
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DN_API DN_OSTLSTMem DN_OS_TLSGetTMem(void const *conflict_arena, DN_OSTLSPushTMem push_tmem)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_U8 tls_index = (DN_U8)-1;
|
||||
for (DN_U8 index = DN_OSTLSArena_TMem0; index <= DN_OSTLSArena_TMem1; index++) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
tls_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DN_Assert(tls_index != (DN_U8)-1);
|
||||
return DN_OSTLSTMem(tls, tls_index, push_tmem);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSPushArena(DN_Arena *arena)
|
||||
{
|
||||
DN_Assert(arena);
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Assert(tls->arena_stack_index < DN_ArrayCountU(tls->arena_stack));
|
||||
tls->arena_stack[tls->arena_stack_index++] = arena;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSPopArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Assert(tls->arena_stack_index > 0);
|
||||
tls->arena_stack_index--;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSTopArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = nullptr;
|
||||
if (tls->arena_stack_index)
|
||||
result = tls->arena_stack[tls->arena_stack_index - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSBeginFrame(DN_Arena *frame_arena)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
tls->frame_arena = frame_arena;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSFrameArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = tls->frame_arena;
|
||||
DN_AssertF(result, "Frame arena must be set by calling DN_OS_TLSBeginFrame at the beginning of the frame");
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSErrSink ////////////////////////////////////////////////////////////////////////////////
|
||||
static void DN_OS_ErrSinkCheck_(DN_OSErrSink const *err)
|
||||
{
|
||||
DN_AssertF(err->arena, "Arena should be assigned in TLS init");
|
||||
if (err->stack_size == 0)
|
||||
return;
|
||||
|
||||
DN_OSErrSinkNode const *node = err->stack + (err->stack_size - 1);
|
||||
DN_Assert(node->mode >= DN_OSErrSinkMode_Nil && node->mode <= DN_OSErrSinkMode_ExitOnError);
|
||||
DN_Assert(node->msg_sentinel);
|
||||
|
||||
// NOTE: Walk the list ensuring we eventually terminate at the sentinel (e.g. we have a
|
||||
// well formed doubly-linked-list terminated by a sentinel, or otherwise we will hit the
|
||||
// walk limit or dereference a null pointer and assert)
|
||||
size_t WALK_LIMIT = 99'999;
|
||||
size_t walk = 0;
|
||||
for (DN_OSErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next, walk++) {
|
||||
DN_AssertF(it, "Encountered null pointer which should not happen in a sentinel DLL");
|
||||
DN_Assert(walk < WALK_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_OSErrSink *DN_OS_ErrSinkBegin_(DN_OSErrSinkMode mode, DN_CallSite call_site)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_OSErrSink *err = &tls->err_sink;
|
||||
DN_OSErrSink *result = err;
|
||||
DN_USize arena_pos = DN_Arena_Pos(result->arena);
|
||||
|
||||
if (tls->err_sink.stack_size == DN_ArrayCountU(err->stack)) {
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
DN_USize counter = 0;
|
||||
DN_ForItSize(it, DN_OSErrSinkNode, err->stack, err->stack_size) {
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(4) when a string is required in call to 'DN_Str8Builder_AppendF' Actual type: 'struct DN_Str8'.
|
||||
DN_Str8Builder_AppendF(&builder, " [%04zu] %S:%u %S\n", counter++, it.data->call_site.file, it.data->call_site.line, it.data->call_site.function);
|
||||
DN_MSVC_WARNING_POP
|
||||
}
|
||||
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(6) when a string is required in call to 'DN_LOG_EmitFromType' Actual type: 'struct DN_Str8'.
|
||||
DN_AssertF(tls->err_sink.stack_size < DN_ArrayCountU(err->stack),
|
||||
"Error sink has run out of error scopes, potential leak. Scopes were\n%S", DN_Str8Builder_BuildFromTLS(&builder));
|
||||
DN_MSVC_WARNING_POP
|
||||
}
|
||||
|
||||
DN_OSErrSinkNode *node = tls->err_sink.stack + tls->err_sink.stack_size++;
|
||||
node->arena_pos = arena_pos;
|
||||
node->mode = mode;
|
||||
node->call_site = call_site;
|
||||
DN_DLList_InitArena(node->msg_sentinel, DN_OSErrSinkMsg, result->arena);
|
||||
|
||||
// NOTE: Handle allocation error
|
||||
if (!DN_Check(node && node->msg_sentinel)) {
|
||||
DN_Arena_PopTo(result->arena, arena_pos);
|
||||
node->msg_sentinel = nullptr;
|
||||
tls->err_sink.stack_size--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkHasError(DN_OSErrSink *err)
|
||||
{
|
||||
bool result = false;
|
||||
if (err && err->stack_size) {
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
result = DN_DLList_HasItems(node->msg_sentinel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSErrSinkMsg *DN_OS_ErrSinkEnd(DN_Arena *arena, DN_OSErrSink *err)
|
||||
{
|
||||
DN_OSErrSinkMsg *result = nullptr;
|
||||
DN_OS_ErrSinkCheck_(err);
|
||||
if (!err || err->stack_size == 0)
|
||||
return result;
|
||||
|
||||
DN_AssertF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_OSErrSinkMsg *prev = nullptr;
|
||||
for (DN_OSErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next) {
|
||||
DN_OSErrSinkMsg *entry = DN_Arena_New(arena, DN_OSErrSinkMsg, DN_ZeroMem_Yes);
|
||||
entry->msg = DN_Str8_Copy(arena, it->msg);
|
||||
entry->call_site = it->call_site;
|
||||
entry->error_code = it->error_code;
|
||||
if (!result)
|
||||
result = entry; // Assign first entry if we haven't yet
|
||||
if (prev)
|
||||
prev->next = entry; // Link the prev message to the current one
|
||||
prev = entry; // Update prev to latest
|
||||
}
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
err->stack_size--;
|
||||
DN_Arena_PopTo(err->arena, node->arena_pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_OS_ErrSinkAddMsgToStr8Builder_(DN_Str8Builder *builder, DN_OSErrSinkMsg *msg, DN_OSErrSinkMsg *end)
|
||||
{
|
||||
if (msg == end) // NOTE: No error messages to add
|
||||
return;
|
||||
|
||||
if (msg->next == end) {
|
||||
DN_OSErrSinkMsg *it = msg;
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%.*s:%05I32u:%.*s %.*s",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_STR_FMT(it->msg));
|
||||
} else {
|
||||
// NOTE: More than one message
|
||||
for (DN_OSErrSinkMsg *it = msg; it != end; it = it->next) {
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%s - %.*s:%05I32u:%.*s%s%.*s",
|
||||
it == msg ? "" : "\n",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_Str8_HasData(it->msg) ? " " : "",
|
||||
DN_STR_FMT(it->msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_ErrSinkEndStr8(DN_Arena *arena, DN_OSErrSink *err)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
DN_OS_ErrSinkCheck_(err);
|
||||
if (!err || err->stack_size == 0)
|
||||
return result;
|
||||
|
||||
DN_AssertF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_OS_ErrSinkAddMsgToStr8Builder_(&builder, node->msg_sentinel->next, node->msg_sentinel);
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
err->stack_size--;
|
||||
DN_U64 arena_pos = node->arena_pos;
|
||||
DN_Arena_PopTo(err->arena, arena_pos);
|
||||
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndIgnore(DN_OSErrSink *err)
|
||||
{
|
||||
DN_OS_ErrSinkEnd(nullptr, err);
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogError_(DN_OSErrSink *err, DN_CallSite call_site, DN_Str8 err_msg)
|
||||
{
|
||||
DN_AssertF(err->stack_size, "Begin must be called before calling end");
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_AssertF(node->msg_sentinel, "Begin must be called before calling end");
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
||||
DN_OSErrSinkMode mode = node->mode;
|
||||
DN_OSErrSinkMsg *msg = DN_OS_ErrSinkEnd(tmem.arena, err);
|
||||
if (!msg)
|
||||
return false;
|
||||
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
if (DN_Str8_HasData(err_msg)) {
|
||||
DN_Str8Builder_AppendRef(&builder, err_msg);
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8(":"));
|
||||
} else {
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("Error(s) encountered:"));
|
||||
}
|
||||
|
||||
if (msg->next) // NOTE: More than 1 message
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("\n"));
|
||||
DN_OS_ErrSinkAddMsgToStr8Builder_(&builder, msg, nullptr);
|
||||
|
||||
DN_Str8 log = DN_Str8Builder_BuildFromTLS(&builder);
|
||||
DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Error), call_site, "%.*s", DN_STR_FMT(log));
|
||||
|
||||
if (mode == DN_OSErrSinkMode_DebugBreakOnEndAndLog)
|
||||
DN_DebugBreak;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorFV_(DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_ErrSinkEndAndLogError_(err, call_site, log);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorF_(DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_ErrSinkEndAndLogError_(err, call_site, log);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorFV_(DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (DN_OS_ErrSinkEndAndLogErrorFV_(err, call_site, fmt, args)) {
|
||||
DN_DebugBreak;
|
||||
DN_OS_Exit(exit_val);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorF_(DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_ErrSinkEndAndExitIfErrorFV_(err, call_site, exit_val, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkAppendFV_(DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
DN_Assert(err && err->stack_size);
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_AssertF(node, "Error sink must be begun by calling 'Begin' before using this function.");
|
||||
|
||||
DN_OSErrSinkMsg *msg = DN_Arena_New(err->arena, DN_OSErrSinkMsg, DN_ZeroMem_Yes);
|
||||
if (DN_Check(msg)) {
|
||||
msg->msg = DN_Str8_InitFV(err->arena, fmt, args);
|
||||
msg->error_code = error_code;
|
||||
msg->call_site = DN_OS_TLSGet()->call_site;
|
||||
DN_DLList_Prepend(node->msg_sentinel, msg);
|
||||
|
||||
if (node->mode == DN_OSErrSinkMode_ExitOnError)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkAppendF_(DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_ErrSinkAppendFV_(err, error_code, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
139
OS/dn_os_tls.h
Normal file
139
OS/dn_os_tls.h
Normal file
@ -0,0 +1,139 @@
|
||||
#if !defined(DN_OS_TLS_H)
|
||||
#define DN_OS_TLS_H
|
||||
|
||||
// NOTE: DN_OSErrSink /////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSErrSinkMode
|
||||
{
|
||||
DN_OSErrSinkMode_Nil, // Default behaviour to accumulate errors into the sink
|
||||
DN_OSErrSinkMode_DebugBreakOnEndAndLog, // Debug break (int3) when error is encountered and the sink is ended by the 'end and log' functions.
|
||||
DN_OSErrSinkMode_ExitOnError, // When an error is encountered, exit the program with the error code of the error that was caught.
|
||||
};
|
||||
|
||||
struct DN_OSErrSinkMsg
|
||||
{
|
||||
DN_I32 error_code;
|
||||
DN_Str8 msg;
|
||||
DN_CallSite call_site;
|
||||
DN_OSErrSinkMsg *next;
|
||||
DN_OSErrSinkMsg *prev;
|
||||
};
|
||||
|
||||
struct DN_OSErrSinkNode
|
||||
{
|
||||
DN_CallSite call_site; // Call site that the node was created
|
||||
DN_OSErrSinkMode mode; // Controls how the sink behaves when an error is registered onto the sink.
|
||||
DN_OSErrSinkMsg *msg_sentinel; // List of error messages accumulated for the current scope
|
||||
DN_U64 arena_pos; // Position to reset the arena when the scope is ended
|
||||
};
|
||||
|
||||
struct DN_OSErrSink
|
||||
{
|
||||
DN_Arena * arena; // Dedicated allocator from the thread's local storage
|
||||
DN_OSErrSinkNode stack[128]; // Each entry contains errors accumulated between a [begin, end] region of the active sink.
|
||||
DN_USize stack_size;
|
||||
};
|
||||
|
||||
enum DN_OSTLSArena
|
||||
{
|
||||
DN_OSTLSArena_Main, // NOTE: Arena for permanent allocations
|
||||
DN_OSTLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
|
||||
|
||||
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
|
||||
DN_OSTLSArena_TMem0,
|
||||
DN_OSTLSArena_TMem1,
|
||||
|
||||
DN_OSTLSArena_Count,
|
||||
};
|
||||
|
||||
struct DN_OSTLS
|
||||
{
|
||||
DN_B32 init; // Flag to track if TLS has been initialised
|
||||
DN_U64 thread_id;
|
||||
DN_CallSite call_site; // Stores call-site information when requested by thread
|
||||
DN_OSErrSink err_sink; // Error handling state
|
||||
DN_Arena arenas[DN_OSTLSArena_Count]; // Default arenas that the thread has access to implicitly
|
||||
DN_Arena * arena_stack[8]; // Active stack of arenas push/popped arenas on into the TLS
|
||||
DN_USize arena_stack_index;
|
||||
|
||||
DN_Arena * frame_arena;
|
||||
char name[64];
|
||||
DN_U8 name_size;
|
||||
};
|
||||
|
||||
// Push the temporary memory arena when retrieved, popped when the arena goes
|
||||
// out of scope. Pushed arenas are used automatically as the allocator in TLS
|
||||
// suffixed function.
|
||||
enum DN_OSTLSPushTMem
|
||||
{
|
||||
DN_OSTLSPushTMem_No,
|
||||
DN_OSTLSPushTMem_Yes,
|
||||
};
|
||||
|
||||
struct DN_OSTLSTMem
|
||||
{
|
||||
DN_OSTLSTMem(DN_OSTLS *context, uint8_t context_index, DN_OSTLSPushTMem push_scratch);
|
||||
~DN_OSTLSTMem();
|
||||
DN_Arena *arena;
|
||||
DN_B32 destructed;
|
||||
DN_OSTLSPushTMem push_arena;
|
||||
DN_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
struct DN_OSTLSInitArgs
|
||||
{
|
||||
DN_U64 reserve;
|
||||
DN_U64 commit;
|
||||
DN_U64 err_sink_reserve;
|
||||
DN_U64 err_sink_commit;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSTLS ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_TLSInit (DN_OSTLS *tls, DN_OSTLSInitArgs args);
|
||||
DN_API void DN_OS_TLSDeinit (DN_OSTLS *tls);
|
||||
DN_API DN_OSTLS * DN_OS_TLSGet ();
|
||||
DN_API void DN_OS_TLSSetCurrentThreadTLS (DN_OSTLS *tls);
|
||||
DN_API DN_Arena * DN_OS_TLSArena ();
|
||||
DN_API DN_OSTLSTMem DN_OS_TLSGetTMem (void const *conflict_arena, DN_OSTLSPushTMem push_tmp_mem);
|
||||
DN_API void DN_OS_TLSPushArena (DN_Arena *arena);
|
||||
DN_API void DN_OS_TLSPopArena ();
|
||||
DN_API DN_Arena * DN_OS_TLSTopArena ();
|
||||
DN_API void DN_OS_TLSBeginFrame (DN_Arena *frame_arena);
|
||||
DN_API DN_Arena * DN_OS_TLSFrameArena ();
|
||||
#define DN_OS_TLSSaveCallSite do { DN_OS_TLSGet()->call_site = DN_CALL_SITE; } while (0)
|
||||
#define DN_OS_TLSTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_No)
|
||||
#define DN_OS_TLSPushTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_Yes)
|
||||
|
||||
// NOTE: DN_OS_ErrSink ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSErrSink * DN_OS_ErrSinkBegin_ (DN_OSErrSinkMode mode, DN_CallSite call_site);
|
||||
#define DN_OS_ErrSinkBegin(mode) DN_OS_ErrSinkBegin_(mode, DN_CALL_SITE)
|
||||
#define DN_OS_ErrSinkBeginDefault() DN_OS_ErrSinkBegin(DN_OSErrSinkMode_Nil)
|
||||
DN_API bool DN_OS_ErrSinkHasError (DN_OSErrSink *err);
|
||||
DN_API DN_OSErrSinkMsg *DN_OS_ErrSinkEnd (DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_Str8 DN_OS_ErrSinkEndStr8 (DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API void DN_OS_ErrSinkEndAndIgnore (DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogError_ (DN_OSErrSink *err, DN_CallSite call_site, DN_Str8 msg);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_OS_ErrSinkAppendFV_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_OS_ErrSinkAppendF_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_ErrSinkEndAndLogError(err, err_msg) DN_OS_ErrSinkEndAndLogError_(err, DN_CALL_SITE, err_msg)
|
||||
#define DN_OS_ErrSinkEndAndLogErrorFV(err, fmt, args) DN_OS_ErrSinkEndAndLogErrorFV_(err, DN_CALL_SITE, fmt, args)
|
||||
#define DN_OS_ErrSinkEndAndLogErrorF(err, fmt, ...) DN_OS_ErrSinkEndAndLogErrorF_(err, DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_ErrSinkEndAndExitIfErrorFV(err, exit_val, fmt, args) DN_OS_ErrSinkEndAndExitIfErrorFV_(err, DN_CALL_SITE, exit_val, fmt, args)
|
||||
#define DN_OS_ErrSinkEndAndExitIfErrorF(err, exit_val, fmt, ...) DN_OS_ErrSinkEndAndExitIfErrorF_(err, DN_CALL_SITE, exit_val, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DN_OS_ErrSinkAppendFV(error, error_code, fmt, args) \
|
||||
do { \
|
||||
DN_OS_TLSSaveCallSite; \
|
||||
DN_OS_ErrSinkAppendFV_(error, error_code, fmt, args); \
|
||||
} while (0)
|
||||
|
||||
#define DN_OS_ErrSinkAppendF(error, error_code, fmt, ...) \
|
||||
do { \
|
||||
DN_OS_TLSSaveCallSite; \
|
||||
DN_OS_ErrSinkAppendF_(error, error_code, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif // defined(DN_OS_TLS_H)
|
1736
OS/dn_os_win32.cpp
Normal file
1736
OS/dn_os_win32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,40 +1,23 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$ | $\ $$ |\_$$ _|$$$\ $$ |$$ ___$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__| $$ |$$$\ $$ | $$ | $$$$\ $$ |\_/ $$ |\__/ $$ |
|
||||
// $$ | $$ |\$$$$$$\ $$ $$ $$\$$ | $$ | $$ $$\$$ | $$$$$ / $$$$$$ |
|
||||
// $$ | $$ | \____$$\ $$$$ _$$$$ | $$ | $$ \$$$$ | \___$$\ $$ ____/
|
||||
// $$ | $$ |$$\ $$ | $$$ / \$$$ | $$ | $$ |\$$$ |$$\ $$ |$$ |
|
||||
// $$$$$$ |\$$$$$$ | $$ / \$$ |$$$$$$\ $$ | \$$ |\$$$$$$ |$$$$$$$$\
|
||||
// \______/ \______/ \__/ \__|\______|\__| \__| \______/ \________|
|
||||
//
|
||||
// dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
#if !defined(DN_OS_WIN32_H)
|
||||
#define DN_OS_WIN32_H
|
||||
|
||||
struct DN_WinError
|
||||
{
|
||||
unsigned long code;
|
||||
DN_Str8 msg;
|
||||
unsigned long code;
|
||||
DN_Str8 msg;
|
||||
};
|
||||
|
||||
// NOTE: Windows Str8 <-> Str16 ///////////////////////////////////////////
|
||||
struct DN_Win_FolderIteratorW
|
||||
{
|
||||
void *handle;
|
||||
DN_Str16 file_name;
|
||||
wchar_t file_name_buf[512];
|
||||
void *handle;
|
||||
DN_Str16 file_name;
|
||||
wchar_t file_name_buf[512];
|
||||
};
|
||||
|
||||
DN_API void DN_Win_ThreadSetName (DN_Str8 name);
|
||||
|
||||
// NOTE: [$WIND] DN_Win ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Win ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str16 DN_Win_ErrorCodeToMsg16Alloc(uint32_t error_code);
|
||||
DN_API DN_WinError DN_Win_ErrorCodeToMsg (DN_Arena *arena, uint32_t error_code);
|
||||
DN_API DN_WinError DN_Win_ErrorCodeToMsgAlloc (uint32_t error_code);
|
||||
@ -47,7 +30,7 @@ DN_API DN_Str16 DN_Win_Str8ToStr16 (DN_Arena *arena, DN_Str8 src);
|
||||
DN_API int DN_Win_Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size);
|
||||
DN_API DN_Str8 DN_Win_Str16ToStr8 (DN_Arena *arena, DN_Str16 src);
|
||||
DN_API int DN_Win_Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size);
|
||||
DN_API DN_Str8 DN_Win_Str16ToStr8Alloc (DN_Str16 src);
|
||||
DN_API DN_Str8 DN_Win_Str16ToStr8FromHeap(DN_Str16 src);
|
||||
|
||||
// NOTE: Path navigation ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str16 DN_Win_EXEPathW (DN_Arena *arena);
|
||||
@ -55,3 +38,4 @@ DN_API DN_Str16 DN_Win_EXEDirW (DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Win_WorkingDir (DN_Arena *arena, DN_Str8 suffix);
|
||||
DN_API DN_Str16 DN_Win_WorkingDirW (DN_Arena *arena, DN_Str16 suffix);
|
||||
DN_API bool DN_Win_DirWIterate (DN_Str16 path, DN_Win_FolderIteratorW *it);
|
||||
#endif // !defined(DN_OS_WIN32)
|
1325
OS/dn_os_windows.h
Normal file
1325
OS/dn_os_windows.h
Normal file
File diff suppressed because it is too large
Load Diff
271
SIMD/dn_simd_avx512f.cpp
Normal file
271
SIMD/dn_simd_avx512f.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
#define DN_SIMD_AVX512F_CPP
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
DN_API DN_Str8FindResult DN_SIMD_Str8FindAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
|
||||
DN_Str8FindResult result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size)
|
||||
return result;
|
||||
|
||||
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
|
||||
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
|
||||
|
||||
DN_USize const search_size = string.size - find.size;
|
||||
DN_USize simd_iterations = search_size / sizeof(__m512i);
|
||||
char const *ptr = string.data;
|
||||
|
||||
while (simd_iterations--) {
|
||||
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
|
||||
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
|
||||
|
||||
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
|
||||
// where matches are found.
|
||||
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
|
||||
|
||||
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
|
||||
// operation using the ternarylogic intrinsic.
|
||||
//
|
||||
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
|
||||
// B = find_last_ch_block
|
||||
// C = find_last_ch
|
||||
//
|
||||
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
|
||||
//
|
||||
// / A / B / C / B ^ C / A | (B ^ C) /
|
||||
// | 0 | 0 | 0 | 0 | 0 |
|
||||
// | 0 | 0 | 1 | 1 | 1 |
|
||||
// | 0 | 1 | 0 | 1 | 1 |
|
||||
// | 0 | 1 | 1 | 0 | 0 |
|
||||
// | 1 | 0 | 0 | 0 | 1 |
|
||||
// | 1 | 0 | 1 | 1 | 1 |
|
||||
// | 1 | 1 | 0 | 1 | 1 |
|
||||
// | 1 | 1 | 1 | 0 | 1 |
|
||||
|
||||
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
|
||||
|
||||
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
|
||||
// out which 32 bit elements in the vector had zero bytes. This uses a
|
||||
// bit twiddling trick
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
|
||||
__mmask16 zero_byte_mask = {};
|
||||
{
|
||||
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
|
||||
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
|
||||
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
|
||||
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
|
||||
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
|
||||
}
|
||||
|
||||
while (zero_byte_mask) {
|
||||
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
|
||||
char const *base_ptr = ptr + (4 * lsb_zero_pos);
|
||||
|
||||
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data;
|
||||
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 1;
|
||||
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 2;
|
||||
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 3;
|
||||
}
|
||||
|
||||
if (result.found) {
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, result.index);
|
||||
result.match = DN_Str8_Init(string.data + result.index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - result.index);
|
||||
result.after_match_to_end_of_buffer = DN_Str8_Advance(result.match_to_end_of_buffer, find.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
|
||||
}
|
||||
|
||||
ptr += sizeof(__m512i);
|
||||
}
|
||||
|
||||
for (DN_USize index = ptr - string.data; index < string.size; index++) {
|
||||
DN_Str8 string_slice = DN_Str8_Slice(string, index, find.size);
|
||||
if (DN_Str8_Eq(string_slice, find)) {
|
||||
result.found = true;
|
||||
result.index = index;
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, index);
|
||||
result.match = DN_Str8_Init(string.data + index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - index);
|
||||
result.after_match_to_end_of_buffer = DN_Str8_Advance(result.match_to_end_of_buffer, find.size);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8FindResult DN_SIMD_Str8FindLastAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
|
||||
DN_Str8FindResult result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size)
|
||||
return result;
|
||||
|
||||
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
|
||||
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
|
||||
|
||||
DN_USize const search_size = string.size - find.size;
|
||||
DN_USize simd_iterations = search_size / sizeof(__m512i);
|
||||
char const *ptr = string.data + search_size + 1;
|
||||
|
||||
while (simd_iterations--) {
|
||||
ptr -= sizeof(__m512i);
|
||||
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
|
||||
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
|
||||
|
||||
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
|
||||
// where matches are found.
|
||||
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
|
||||
|
||||
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
|
||||
// operation using the ternarylogic intrinsic.
|
||||
//
|
||||
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
|
||||
// B = find_last_ch_block
|
||||
// C = find_last_ch
|
||||
//
|
||||
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
|
||||
//
|
||||
// / A / B / C / B ^ C / A | (B ^ C) /
|
||||
// | 0 | 0 | 0 | 0 | 0 |
|
||||
// | 0 | 0 | 1 | 1 | 1 |
|
||||
// | 0 | 1 | 0 | 1 | 1 |
|
||||
// | 0 | 1 | 1 | 0 | 0 |
|
||||
// | 1 | 0 | 0 | 0 | 1 |
|
||||
// | 1 | 0 | 1 | 1 | 1 |
|
||||
// | 1 | 1 | 0 | 1 | 1 |
|
||||
// | 1 | 1 | 1 | 0 | 1 |
|
||||
|
||||
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
|
||||
|
||||
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
|
||||
// out which 32 bit elements in the vector had zero bytes. This uses a
|
||||
// bit twiddling trick
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
|
||||
__mmask16 zero_byte_mask = {};
|
||||
{
|
||||
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
|
||||
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
|
||||
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
|
||||
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
|
||||
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
|
||||
}
|
||||
|
||||
while (zero_byte_mask) {
|
||||
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
|
||||
char const *base_ptr = ptr + (4 * lsb_zero_pos);
|
||||
|
||||
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data;
|
||||
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 1;
|
||||
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 2;
|
||||
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 3;
|
||||
}
|
||||
|
||||
if (result.found) {
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, result.index);
|
||||
result.match = DN_Str8_Init(string.data + result.index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - result.index);
|
||||
return result;
|
||||
}
|
||||
|
||||
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
|
||||
}
|
||||
}
|
||||
|
||||
for (DN_USize index = ptr - string.data - 1; index < string.size; index--) {
|
||||
DN_Str8 string_slice = DN_Str8_Slice(string, index, find.size);
|
||||
if (DN_Str8_Eq(string_slice, find)) {
|
||||
result.found = true;
|
||||
result.index = index;
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, index);
|
||||
result.match = DN_Str8_Init(string.data + index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - index);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8BinarySplitResult DN_SIMD_Str8BinarySplitAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
DN_Str8BinarySplitResult result = {};
|
||||
DN_Str8FindResult find_result = DN_SIMD_Str8FindAVX512F(string, find);
|
||||
if (find_result.found) {
|
||||
result.lhs.data = string.data;
|
||||
result.lhs.size = find_result.index;
|
||||
result.rhs = DN_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
|
||||
} else {
|
||||
result.lhs = string;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8BinarySplitResult DN_SIMD_Str8BinarySplitLastAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
DN_Str8BinarySplitResult result = {};
|
||||
DN_Str8FindResult find_result = DN_SIMD_Str8FindLastAVX512F(string, find);
|
||||
if (find_result.found) {
|
||||
result.lhs.data = string.data;
|
||||
result.lhs.size = find_result.index;
|
||||
result.rhs = DN_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
|
||||
} else {
|
||||
result.lhs = string;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_SIMD_Str8SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_USize result = 0; // The number of splits in the actual string.
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(delimiter) || delimiter.size <= 0)
|
||||
return result;
|
||||
|
||||
DN_Str8BinarySplitResult split = {};
|
||||
DN_Str8 first = string;
|
||||
do {
|
||||
split = DN_SIMD_Str8BinarySplitAVX512F(first, delimiter);
|
||||
if (split.lhs.size || mode == DN_Str8SplitIncludeEmptyStrings_Yes) {
|
||||
if (splits && result < splits_count)
|
||||
splits[result] = split.lhs;
|
||||
result++;
|
||||
}
|
||||
first = split.rhs;
|
||||
} while (first.size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_SIMD_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = {};
|
||||
DN_USize splits_required = DN_SIMD_Str8SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
|
||||
result.data = DN_Arena_NewArray(arena, DN_Str8, splits_required, DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
result.size = DN_SIMD_Str8SplitAVX512F(string, delimiter, result.data, splits_required, mode);
|
||||
DN_Assert(splits_required == result.size);
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#if !defined(DN_AVX512F_H)
|
||||
#define DN_AVX512F_H
|
||||
#if !defined(DN_SIMD_AVX512F_H)
|
||||
#define DN_SIMD_AVX512F_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -13,13 +13,11 @@
|
||||
// $$ | $$ | \$ / $$ / $$ | \$$$$$$ |$$$$$$\ $$$$$$$$\ $$ |
|
||||
// \__| \__| \_/ \__| \__| \______/ \______|\________|\__|
|
||||
//
|
||||
// dqn_avx512f.h -- Functions implemented w/ AVX512
|
||||
// dn_avx512f.h -- Functions implemented w/ AVX512
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#include "dqn.h"
|
||||
|
||||
DN_API DN_Str8FindResult DN_Str8_FindStr8AVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_Str8FindResult DN_Str8_FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitAVX512F (DN_Str8 string, DN_Str8 find);
|
||||
@ -27,4 +25,4 @@ DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitLastAVX512F(DN_Str8 string, D
|
||||
DN_API DN_USize DN_Str8_SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
|
||||
#endif // DN_AVX512F_H
|
||||
#endif // DN_SIMD_AVX512F_H
|
@ -1,19 +1,3 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$ __$$\ $$ _____|\_$$ _|$$ | $$ _____|
|
||||
// $$ / \__|$$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | $$$$$$$ |$$$$$$$ | $$$$$\ $$ | $$ | $$$$$\
|
||||
// $$ | $$ ____/ $$ ____/ $$ __| $$ | $$ | $$ __|
|
||||
// $$ | $$\ $$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// \$$$$$$ |$$ | $$ | $$ | $$$$$$\ $$$$$$$$\ $$$$$$$$\
|
||||
// \______/ \__| \__| \__| \______|\________|\________|
|
||||
//
|
||||
// dn_cpp_file.h -- Functions to emit C++ formatted code
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
#if !defined(DN_CPP_FILE_H)
|
||||
#define DN_CPP_FILE_H
|
||||
|
666
Standalone/dn_keccak.h
Normal file
666
Standalone/dn_keccak.h
Normal file
@ -0,0 +1,666 @@
|
||||
#if !defined(DN_KC_H)
|
||||
#define DN_KC_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ | $$ |$$ _____|$$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ |
|
||||
// $$ |$$ / $$ | $$ / \__|$$ / \__|$$ / $$ |$$ |$$ /
|
||||
// $$$$$ / $$$$$\ $$ | $$ | $$$$$$$$ |$$$$$ /
|
||||
// $$ $$< $$ __| $$ | $$ | $$ __$$ |$$ $$<
|
||||
// $$ |\$$\ $$ | $$ | $$\ $$ | $$\ $$ | $$ |$$ |\$$\
|
||||
// $$ | \$$\ $$$$$$$$\ \$$$$$$ |\$$$$$$ |$$ | $$ |$$ | \$$\
|
||||
// \__| \__|\________| \______/ \______/ \__| \__|\__| \__|
|
||||
//
|
||||
// dn_keccak.h -- FIPS202 SHA3 + non-finalized SHA3 (aka. Keccak) hashing algorithms
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Implementation of the Keccak hashing algorithms from the Keccak and SHA3
|
||||
// families (including the FIPS202 published algorithms and the non-finalized
|
||||
// ones, i.e. the ones used in Ethereum and Monero which adopted SHA3 before it
|
||||
// was finalized. The only difference between the 2 is a different delimited
|
||||
// suffix).
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2021 github.com/doy-lee
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
|
||||
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
|
||||
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \______| \______/ \__| \__| \______/
|
||||
//
|
||||
// Options -- Compile time build customisation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// - Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file.
|
||||
//
|
||||
// #define DN_KC_IMPLEMENTATION
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(DN_KC_MEMCPY)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMCPY(dest, src, count) memcpy(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_MEMCMP)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMCMP(dest, src, count) memcmp(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_MEMSET)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMSET(dest, byte, count) memset(dest, byte, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_ASSERT)
|
||||
#if defined(NDEBUG)
|
||||
#define DN_KC_ASSERT(expr)
|
||||
#else
|
||||
#define DN_KC_ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
(*(volatile int *)0) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// Use this macro in a printf-like function,
|
||||
/*
|
||||
DN_KCString64 string = {};
|
||||
printf("%.*s\n", DN_KC_STRING64_FMT(string));
|
||||
*/
|
||||
#define DN_KC_STRING56_FMT(string) 56, string
|
||||
#define DN_KC_STRING64_FMT(string) 64, string
|
||||
#define DN_KC_STRING96_FMT(string) 96, string
|
||||
#define DN_KC_STRING128_FMT(string) 128, string
|
||||
|
||||
typedef struct DN_KCBytes28 { char data[28]; } DN_KCBytes28; // 224 bit
|
||||
typedef struct DN_KCBytes32 { char data[32]; } DN_KCBytes32; // 256 bit
|
||||
typedef struct DN_KCBytes48 { char data[48]; } DN_KCBytes48; // 384 bit
|
||||
typedef struct DN_KCBytes64 { char data[64]; } DN_KCBytes64; // 512 bit
|
||||
|
||||
typedef struct DN_KCString56 { char data[(sizeof(DN_KCBytes28) * 2) + 1]; } DN_KCString56;
|
||||
typedef struct DN_KCString64 { char data[(sizeof(DN_KCBytes32) * 2) + 1]; } DN_KCString64;
|
||||
typedef struct DN_KCString96 { char data[(sizeof(DN_KCBytes48) * 2) + 1]; } DN_KCString96;
|
||||
typedef struct DN_KCString128 { char data[(sizeof(DN_KCBytes64) * 2) + 1]; } DN_KCString128;
|
||||
|
||||
#define DN_KC_LANE_SIZE_U64 5
|
||||
typedef struct DN_KCState {
|
||||
uint8_t state[DN_KC_LANE_SIZE_U64 * DN_KC_LANE_SIZE_U64 * sizeof(uint64_t)];
|
||||
int state_size; // The number of bytes written to the state
|
||||
int absorb_size; // The amount of bytes to absorb/sponge in/from the state
|
||||
int hash_size_bits; // The size of the hash the context was initialised for in bits
|
||||
char delimited_suffix; // The delimited suffix of the current hash
|
||||
} DN_KCState;
|
||||
|
||||
// NOTE: SHA3/Keccak Streaming API /////////////////////////////////////////////////////////////////
|
||||
// Setup a hashing state for either
|
||||
// - FIPS 202 SHA3
|
||||
// - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3)
|
||||
// The non-finalized SHA3 version is the one adopted by many cryptocurrencies
|
||||
// such as Ethereum and Monero as they adopted SHA3 before it was finalized.
|
||||
//
|
||||
// The state produced from this function is to be used alongside the
|
||||
// 'KeccakUpdate' and 'KC_HashFinish' functions.
|
||||
//
|
||||
// sha3: If true, setup the state for FIPS 202 SHA3, otherwise the non-finalized version
|
||||
// hash_size_bits: The number of bits to setup the context for, available sizes are 224, 256, 384 and 512.
|
||||
DN_KCState DN_KC_HashInit(bool sha3, uint16_t hash_size_bits);
|
||||
|
||||
// After initialising a 'DN_KCState' via 'DN_KeccakSHA3Init', iteratively
|
||||
// update the hash with new data by calling 'DN_KC_HashUpdate'. On completion,
|
||||
// call 'DN_KC_HashFinish' to generate the hash from the state. The 'dest_size'
|
||||
// must be at-least the (bit-size / 8), i.e. for 'DN_Keccak512Init' it must be
|
||||
// atleast (512 / 8) bytes, 64 bytes.
|
||||
void DN_KC_HashUpdate(DN_KCState *keccak, void const *data, size_t data_size);
|
||||
DN_KCBytes32 DN_KC_HashFinish(DN_KCState const *keccak);
|
||||
void DN_KC_HashFinishPtr(DN_KCState *keccak, void *dest, size_t dest_size);
|
||||
|
||||
// NOTE: Simple API ////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper function that combines the Init, Update and Finish step in one shot,
|
||||
// i.e. hashing a singlular contiguous buffer. Use the streaming API if data
|
||||
// is split across different buffers.
|
||||
void DN_KC_Hash(bool sha3, uint16_t hash_size_bits, void const *src, uint64_t src_size, void *dest, int dest_size);
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: SHA3 Streaming API
|
||||
#define DN_KC_SHA3_224Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 224)
|
||||
#define DN_KC_SHA3_224Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_224Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_224FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_256Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 256)
|
||||
#define DN_KC_SHA3_256Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_256Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_256FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_384Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 384)
|
||||
#define DN_KC_SHA3_384Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_384Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_384FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_512Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 512)
|
||||
#define DN_KC_SHA3_512Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_512Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_512FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
// NOTE: SHA3 one-shot API
|
||||
#define DN_KC_SHA3_224Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 224, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_256Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 256, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_384Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 384, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_512Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 512, src, size, dest, dest_size)
|
||||
|
||||
DN_KCBytes28 DN_KC_SHA3_224(void const *src, uint64_t size);
|
||||
DN_KCBytes32 DN_KC_SHA3_256(void const *src, uint64_t size);
|
||||
DN_KCBytes48 DN_KC_SHA3_384(void const *src, uint64_t size);
|
||||
DN_KCBytes64 DN_KC_SHA3_512(void const *src, uint64_t size);
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: SHA3 Streaming API
|
||||
#define DN_KC_Keccak224Init() DN_KC_HashInit(false /*sha3*/, 224)
|
||||
#define DN_KC_Keccak224Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak224Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak224FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak256Init() DN_KC_HashInit(false /*sha3*/, 256)
|
||||
#define DN_KC_Keccak256Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak256Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak256FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak384Init() DN_KC_HashInit(false /*sha3*/, 384)
|
||||
#define DN_KC_Keccak384Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak384Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak384FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak512Init() DN_KC_HashInit(false /*sha3*/, 512)
|
||||
#define DN_KC_Keccak512Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak512Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak512FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
// NOTE: SHA3 one-shot API
|
||||
#define DN_KC_Keccak224Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 224, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak256Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 256, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak384Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 384, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak512Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 512, src, size, dest, dest_size)
|
||||
|
||||
DN_KCBytes28 DN_KC_Keccak224(void const *src, uint64_t size);
|
||||
DN_KCBytes32 DN_KC_Keccak256(void const *src, uint64_t size);
|
||||
DN_KCBytes48 DN_KC_Keccak384(void const *src, uint64_t size);
|
||||
DN_KCBytes64 DN_KC_Keccak512(void const *src, uint64_t size);
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224Str8(DN_Str8 string);
|
||||
DN_KCBytes32 DN_KC_SHA3_256Str8(DN_Str8 string);
|
||||
DN_KCBytes48 DN_KC_SHA3_384Str8(DN_Str8 string);
|
||||
DN_KCBytes64 DN_KC_SHA3_512Str8(DN_Str8 string);
|
||||
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224Str8(DN_Str8 string);
|
||||
DN_KCBytes32 DN_KC_Keccak256Str8(DN_Str8 string);
|
||||
DN_KCBytes48 DN_KC_Keccak384Str8(DN_Str8 string);
|
||||
DN_KCBytes64 DN_KC_Keccak512Str8(DN_Str8 string);
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
// NOTE: Helper functions //////////////////////////////////////////////////////////////////////////
|
||||
// Convert a binary buffer into its hex representation into dest. The dest
|
||||
// buffer must be large enough to contain the hex representation, i.e.
|
||||
// atleast src_size * 2). This function does *not* null-terminate the buffer.
|
||||
void DN_KC_BytesToHex(void const *src, uint64_t src_size, char *dest, uint64_t dest_size);
|
||||
|
||||
// Converts a fixed amount of bytes into a hexadecimal string. Helper functions
|
||||
// that call into DN_KCBytesToHex.
|
||||
// return: The hexadecimal string of the bytes, null-terminated.
|
||||
DN_KCString56 DN_KC_Bytes28ToHex(DN_KCBytes28 const *bytes);
|
||||
DN_KCString64 DN_KC_Bytes32ToHex(DN_KCBytes32 const *bytes);
|
||||
DN_KCString96 DN_KC_Bytes48ToHex(DN_KCBytes48 const *bytes);
|
||||
DN_KCString128 DN_KC_Bytes64ToHex(DN_KCBytes64 const *bytes);
|
||||
|
||||
// Compares byte data structures for byte equality (via memcmp).
|
||||
// return: 1 if the contents are equal otherwise 0.
|
||||
int DN_KC_Bytes28Equals(DN_KCBytes28 const *a, DN_KCBytes28 const *b);
|
||||
int DN_KC_Bytes32Equals(DN_KCBytes32 const *a, DN_KCBytes32 const *b);
|
||||
int DN_KC_Bytes48Equals(DN_KCBytes48 const *a, DN_KCBytes48 const *b);
|
||||
int DN_KC_Bytes64Equals(DN_KCBytes64 const *a, DN_KCBytes64 const *b);
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Other helper functions for DN data structures ////////////////////////////////////////////
|
||||
// Converts a 64 character hex string into the 32 byte binary representation.
|
||||
// Invalid hex characters in the string will be represented as 0.
|
||||
// hex: Must be exactly a 64 character hex string.
|
||||
DN_KCBytes32 DN_KC_Hex64ToBytes(DN_Str8 hex);
|
||||
#endif // DN_BASE_STRING_H
|
||||
#endif // DN_KC_H
|
||||
|
||||
#if defined(DN_KC_IMPLEMENTATION)
|
||||
uint64_t const DN_KC_ROUNDS[] = {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B,
|
||||
0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088,
|
||||
0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
};
|
||||
|
||||
uint64_t const DN_KC_ROTATIONS[][5] =
|
||||
{
|
||||
{0, 36, 3, 41, 18},
|
||||
{1, 44, 10, 45, 2},
|
||||
{62, 6, 43, 15, 61},
|
||||
{28, 55, 25, 21, 56},
|
||||
{27, 20, 39, 8, 14},
|
||||
};
|
||||
|
||||
#define DN_KC_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate)))))
|
||||
|
||||
static void DN_KC_Permute_(void *state)
|
||||
{
|
||||
// TODO(dn): Do some profiling on unrolling and can we SIMD some part of
|
||||
// this? Unroll loop, look at data dependencies and investigate.
|
||||
|
||||
uint64_t *lanes_u64 = (uint64_t *)state;
|
||||
for (int round_index = 0; round_index < 24; round_index++)
|
||||
{
|
||||
#define LANE_INDEX(x, y) ((x) + ((y) * DN_KC_LANE_SIZE_U64))
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
#if 1
|
||||
uint64_t c[DN_KC_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
c[x] = lanes_u64[LANE_INDEX(x, 0)] ^
|
||||
lanes_u64[LANE_INDEX(x, 1)] ^
|
||||
lanes_u64[LANE_INDEX(x, 2)] ^
|
||||
lanes_u64[LANE_INDEX(x, 3)] ^
|
||||
lanes_u64[LANE_INDEX(x, 4)];
|
||||
}
|
||||
|
||||
uint64_t d[DN_KC_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++)
|
||||
d[x] = c[(x + 4) % DN_KC_LANE_SIZE_U64] ^ DN_KC_ROL64(c[(x + 1) % DN_KC_LANE_SIZE_U64], 1);
|
||||
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++)
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++)
|
||||
lanes_u64[LANE_INDEX(x, y)] ^= d[x];
|
||||
#else
|
||||
uint64_t c[5], d[5];
|
||||
c[0] = lanes_u64[0 * 5 + 0] ^ lanes_u64[1 * 5 + 0] ^ lanes_u64[2 * 5 + 0] ^ lanes_u64[3 * 5 + 0] ^ lanes_u64[4 * 5 + 0];
|
||||
c[1] = lanes_u64[0 * 5 + 1] ^ lanes_u64[1 * 5 + 1] ^ lanes_u64[2 * 5 + 1] ^ lanes_u64[3 * 5 + 1] ^ lanes_u64[4 * 5 + 1];
|
||||
c[2] = lanes_u64[0 * 5 + 2] ^ lanes_u64[1 * 5 + 2] ^ lanes_u64[2 * 5 + 2] ^ lanes_u64[3 * 5 + 2] ^ lanes_u64[4 * 5 + 2];
|
||||
c[3] = lanes_u64[0 * 5 + 3] ^ lanes_u64[1 * 5 + 3] ^ lanes_u64[2 * 5 + 3] ^ lanes_u64[3 * 5 + 3] ^ lanes_u64[4 * 5 + 3];
|
||||
c[4] = lanes_u64[0 * 5 + 4] ^ lanes_u64[1 * 5 + 4] ^ lanes_u64[2 * 5 + 4] ^ lanes_u64[3 * 5 + 4] ^ lanes_u64[4 * 5 + 4];
|
||||
|
||||
d[0] = c[4] ^ DN_KC_ROL64(c[1], 1);
|
||||
d[1] = c[0] ^ DN_KC_ROL64(c[2], 1);
|
||||
d[2] = c[1] ^ DN_KC_ROL64(c[3], 1);
|
||||
d[3] = c[2] ^ DN_KC_ROL64(c[4], 1);
|
||||
d[4] = c[3] ^ DN_KC_ROL64(c[0], 1);
|
||||
#endif
|
||||
|
||||
// ?? and ?? steps ///////////////////////////////////////////////////////////////////////////
|
||||
uint64_t b[DN_KC_LANE_SIZE_U64 * DN_KC_LANE_SIZE_U64];
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++) {
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
uint64_t lane = lanes_u64[LANE_INDEX(x, y)];
|
||||
uint64_t rotate_count = DN_KC_ROTATIONS[x][y];
|
||||
b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DN_KC_ROL64(lane, rotate_count);
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++) {
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
uint64_t rhs = ~b[LANE_INDEX((x + 1) % 5, y)] & b[LANE_INDEX((x + 2) % 5, y)];
|
||||
|
||||
lanes_u64[LANE_INDEX(x, y)] = b[LANE_INDEX(x, y)] ^ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
lanes_u64[LANE_INDEX(0, 0)] ^= DN_KC_ROUNDS[round_index];
|
||||
#undef LANE_INDEX
|
||||
#undef DN_KC_ROL64
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Streaming API /////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCState DN_KC_HashInit(bool sha3, uint16_t hash_size_bits)
|
||||
{
|
||||
char const SHA3_DELIMITED_SUFFIX = 0x06;
|
||||
char const KECCAK_DELIMITED_SUFFIX = 0x01;
|
||||
int const bitrate = 1600 - (hash_size_bits * 2);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DN_KCState result = {};
|
||||
#else
|
||||
DN_KCState result = {0};
|
||||
#endif
|
||||
result.hash_size_bits = hash_size_bits;
|
||||
result.absorb_size = bitrate / 8;
|
||||
result.delimited_suffix = sha3 ? SHA3_DELIMITED_SUFFIX : KECCAK_DELIMITED_SUFFIX;
|
||||
DN_KC_ASSERT(bitrate + (hash_size_bits * 2) /*capacity*/ == 1600);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_KC_HashUpdate(DN_KCState *keccak, void const *data, size_t data_size)
|
||||
{
|
||||
uint8_t *state = keccak->state;
|
||||
uint8_t const *ptr = (uint8_t *)data;
|
||||
size_t ptr_size = data_size;
|
||||
while (ptr_size > 0) {
|
||||
size_t space = keccak->absorb_size - keccak->state_size;
|
||||
int bytes_to_absorb = (int)(space < ptr_size ? space : ptr_size);
|
||||
|
||||
for (int index = 0; index < bytes_to_absorb; index++)
|
||||
state[keccak->state_size + index] ^= ptr[index];
|
||||
|
||||
ptr += bytes_to_absorb;
|
||||
keccak->state_size += bytes_to_absorb;
|
||||
ptr_size -= bytes_to_absorb;
|
||||
|
||||
if (keccak->state_size >= keccak->absorb_size) {
|
||||
DN_KC_ASSERT(keccak->state_size == keccak->absorb_size);
|
||||
DN_KC_Permute_(state);
|
||||
keccak->state_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_HashFinish(DN_KCState *keccak)
|
||||
{
|
||||
DN_KCBytes32 result = {};
|
||||
DN_KC_HashFinishPtr(keccak, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_KC_HashFinishPtr(DN_KCState *keccak, void *dest, size_t dest_size)
|
||||
{
|
||||
DN_KC_ASSERT(dest_size >= (size_t)(keccak->hash_size_bits / 8));
|
||||
|
||||
// Sponge Finalization Step: Final padding bit /////////////////////////////////////////////////
|
||||
int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1;
|
||||
int const delimited_suffix_index = keccak->state_size;
|
||||
DN_KC_ASSERT(delimited_suffix_index < keccak->absorb_size);
|
||||
|
||||
uint8_t *state = keccak->state;
|
||||
state[delimited_suffix_index] ^= keccak->delimited_suffix;
|
||||
|
||||
// NOTE: In the reference implementation, it checks that if the
|
||||
// delimited suffix is set to the padding bit (0x80), then we need to
|
||||
// permute twice. Once for the delimited suffix, and a second time for
|
||||
// the "padding" permute.
|
||||
//
|
||||
// However all standard algorithms either specify a 0x01, or 0x06, 0x04
|
||||
// delimited suffix and so forth- so this case is never hit. We can omit
|
||||
// this from the implementation here.
|
||||
|
||||
state[INDEX_OF_0X80_BYTE] ^= 0x80;
|
||||
DN_KC_Permute_(state);
|
||||
|
||||
// Squeeze Step: Squeeze bytes from the state into our hash ////////////////////////////////////
|
||||
uint8_t *dest_u8 = (uint8_t *)dest;
|
||||
size_t const squeeze_count = dest_size / keccak->absorb_size;
|
||||
size_t squeeze_index = 0;
|
||||
for (; squeeze_index < squeeze_count; squeeze_index++) {
|
||||
if (squeeze_index)
|
||||
DN_KC_Permute_(state);
|
||||
DN_KC_MEMCPY(dest_u8, state, keccak->absorb_size);
|
||||
dest_u8 += keccak->absorb_size;
|
||||
}
|
||||
|
||||
// Squeeze Finalisation Step: Remainder bytes in hash //////////////////////////////////////////
|
||||
int const remainder = dest_size % keccak->absorb_size;
|
||||
if (remainder) {
|
||||
if (squeeze_index)
|
||||
DN_KC_Permute_(state);
|
||||
DN_KC_MEMCPY(dest_u8, state, remainder);
|
||||
}
|
||||
}
|
||||
|
||||
void DN_KC_Hash(bool sha3, uint16_t hash_size_bits, void const *src, size_t src_size, void *dest, size_t dest_size)
|
||||
{
|
||||
DN_KCState state = DN_KC_HashInit(sha3, hash_size_bits);
|
||||
DN_KC_HashUpdate(&state, src, src_size);
|
||||
DN_KC_HashFinishPtr(&state, dest, dest_size);
|
||||
}
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_SHA3_224Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_SHA3_256(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_SHA3_256Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_SHA3_384(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_SHA3_384Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_SHA3_512(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_SHA3_512Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_Keccak224Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_Keccak256(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_Keccak256Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_Keccak384(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_Keccak384Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_Keccak512(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_Keccak512Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_SHA3_224Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_SHA3_256Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_SHA3_256Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_SHA3_384Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_SHA3_384Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_SHA3_512Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_SHA3_512Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_Keccak224Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_Keccak256Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_Keccak256Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_Keccak384Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_Keccak384Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_Keccak512Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_Keccak512Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
// NOTE: Helper functions //////////////////////////////////////////////////////////////////////////
|
||||
void DN_KC_BytesToHex(void const *src, size_t src_size, char *dest, size_t dest_size)
|
||||
{
|
||||
(void)src_size; (void)dest_size;
|
||||
DN_KC_ASSERT(dest_size >= src_size * 2);
|
||||
|
||||
unsigned char *src_u8 = (unsigned char *)src;
|
||||
for (size_t src_index = 0, dest_index = 0; src_index < src_size;
|
||||
src_index += 1, dest_index += 2) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index + 0] = hex01 < 10 ? (hex01 + '0') : (hex01 - 10) + 'a';
|
||||
dest[dest_index + 1] = hex02 < 10 ? (hex02 + '0') : (hex02 - 10) + 'a';
|
||||
}
|
||||
}
|
||||
|
||||
DN_KCString56 DN_KC_Bytes28ToHex(DN_KCBytes28 const *bytes)
|
||||
{
|
||||
DN_KCString56 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString64 DN_KC_Bytes32ToHex(DN_KCBytes32 const *bytes)
|
||||
{
|
||||
DN_KCString64 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString96 DN_KC_Bytes48ToHex(DN_KCBytes48 const *bytes)
|
||||
{
|
||||
DN_KCString96 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString128 DN_KC_Bytes64ToHex(DN_KCBytes64 const *bytes)
|
||||
{
|
||||
DN_KCString128 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes28Equals(DN_KCBytes28 const *a, DN_KCBytes28 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes32Equals(DN_KCBytes32 const *a, DN_KCBytes32 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes48Equals(DN_KCBytes48 const *a, DN_KCBytes48 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes64Equals(DN_KCBytes64 const *a, DN_KCBytes64 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Other helper functions for DN data structures ////////////////////////////////////////////
|
||||
DN_KCBytes32 DN_KC_Hex64ToBytes(DN_Str8 hex)
|
||||
{
|
||||
DN_KC_ASSERT(hex.size == 64);
|
||||
DN_KCBytes32 result;
|
||||
DN_CVT_HexToBytesPtr(hex, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
#endif // DN_KC_IMPLEMENTATION
|
330
Standalone/dn_utest.h
Normal file
330
Standalone/dn_utest.h
Normal file
@ -0,0 +1,330 @@
|
||||
#if !defined(DN_UT_H)
|
||||
#define DN_UT_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\
|
||||
// $$ | $$ |\__$$ __|$$ _____|$$ __$$\\__$$ __|
|
||||
// $$ | $$ | $$ | $$ | $$ / \__| $$ |
|
||||
// $$ | $$ | $$ | $$$$$\ \$$$$$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ __| \____$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ | $$\ $$ | $$ |
|
||||
// \$$$$$$ | $$ | $$$$$$$$\ \$$$$$$ | $$ |
|
||||
// \______/ \__| \________| \______/ \__|
|
||||
//
|
||||
// dn_utest.h -- Extremely minimal unit testing framework
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A super minimal testing framework, most of the logic here is the pretty
|
||||
// printing of test results.
|
||||
//
|
||||
// NOTE: Configuration /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// #define DN_UT_IMPLEMENTATION
|
||||
// Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file. This will also automatically enable the JSMN
|
||||
// implementation.
|
||||
//
|
||||
// #define DN_UT_RESULT_LPAD
|
||||
// Define this to a number to specify how much to pad the output of the test
|
||||
// result line before the test result is printed.
|
||||
//
|
||||
// #define DN_UT_RESULT_PAD_CHAR
|
||||
// Define this to a character to specify the default character to use for
|
||||
// padding. By default this is '.'
|
||||
//
|
||||
// #define DN_UT_SPACING
|
||||
// Define this to a number to specify the number of spaces between the group
|
||||
// declaration and the test output in the group.
|
||||
//
|
||||
// #define DN_UT_BAD_COLOR
|
||||
// Define this to a terminal color code to specify what color errors will be
|
||||
// presented as.
|
||||
//
|
||||
// #define DN_UT_GOOD_COLOR
|
||||
// Define this to a terminal color code to specify what color sucess will be
|
||||
// presented as.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(DN_UT_RESULT_LPAD)
|
||||
#define DN_UT_RESULT_LPAD 90
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_RESULT_PAD_CHAR)
|
||||
#define DN_UT_RESULT_PAD_CHAR '.'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_SPACING)
|
||||
#define DN_UT_SPACING 2
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_BAD_COLOR)
|
||||
#define DN_UT_BAD_COLOR "\x1b[31m"
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_GOOD_COLOR)
|
||||
#define DN_UT_GOOD_COLOR "\x1b[32m"
|
||||
#endif
|
||||
|
||||
#define DN_UT_COLOR_RESET "\x1b[0m"
|
||||
|
||||
#define DN_UT_Test(test, fmt, ...) \
|
||||
for (int dummy_ = (DN_UT_BeginF((test), fmt, ##__VA_ARGS__), 0); \
|
||||
(void)dummy_, (test)->state == DN_UTState_TestBegun; \
|
||||
DN_UT_End(test))
|
||||
|
||||
#define DN_UT_AssertF(test, expr, fmt, ...) \
|
||||
DN_UT_AssertAtF((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DN_UT_Assert(test, expr) \
|
||||
DN_UT_AssertAt((test), __FILE__, __LINE__, (expr))
|
||||
|
||||
// TODO: Fix the logs. They print before the tests, we should accumulate logs
|
||||
// per test, then, dump them on test on. But to do this nicely without crappy
|
||||
// mem management we need to implement an arena.
|
||||
#define DN_UT_Log(test, fmt, ...) \
|
||||
DN_UT_LogF(test, "%*s" fmt "\n", DN_UT_SPACING * 2, "", ##__VA_ARGS__)
|
||||
|
||||
#define DN_UT_AssertAtF(test, file, line, expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTState_TestFailed; \
|
||||
DN_UT_LogInsideTestF(test, \
|
||||
"%*sAssertion File: %s:%d\n" \
|
||||
"%*sExpression: [" #expr \
|
||||
"]\n" \
|
||||
"%*sReason: " fmt "\n", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_UT_AssertAt(test, file, line, expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTState_TestFailed; \
|
||||
DN_UT_LogInsideTestF(test, \
|
||||
"%*sAssertion File: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UT_SPACING * 2, \
|
||||
""); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Header ////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum DN_UTState
|
||||
{
|
||||
DN_UTState_Nil,
|
||||
DN_UTState_TestBegun,
|
||||
DN_UTState_TestFailed,
|
||||
} DN_UTState;
|
||||
|
||||
typedef struct DN_UTStr8Link
|
||||
{
|
||||
char *data;
|
||||
size_t size;
|
||||
DN_UTStr8Link *next;
|
||||
DN_UTStr8Link *prev;
|
||||
} DN_UTStr8Link;
|
||||
|
||||
typedef struct DN_UTCore
|
||||
{
|
||||
int num_tests_in_group;
|
||||
int num_tests_ok_in_group;
|
||||
DN_UTState state;
|
||||
char name[256];
|
||||
size_t name_size;
|
||||
DN_UTStr8Link *curr_test_messages;
|
||||
DN_UTStr8Link *output;
|
||||
} DN_UTCore;
|
||||
|
||||
void DN_UT_BeginFV(DN_UTCore *test, char const *fmt, va_list args);
|
||||
void DN_UT_BeginF(DN_UTCore *test, char const *fmt, ...);
|
||||
void DN_UT_End(DN_UTCore *test);
|
||||
|
||||
void DN_UT_LogF(DN_UTCore *test, char const *fmt, ...);
|
||||
void DN_UT_LogInsideTestF(DN_UTCore *test, char const *fmt, ...);
|
||||
|
||||
bool DN_UT_AllTestsPassed(DN_UTCore const *test);
|
||||
void DN_UT_PrintTests(DN_UTCore const *test);
|
||||
#endif // DN_UT_H
|
||||
|
||||
// NOTE: Implementation ////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_UT_IMPLEMENTATION)
|
||||
DN_UTCore DN_UT_Init()
|
||||
{
|
||||
DN_UTCore result = {};
|
||||
result.output = (DN_UTStr8Link *)calloc(1, sizeof(*result.output));
|
||||
result.curr_test_messages = (DN_UTStr8Link *)calloc(1, sizeof(*result.curr_test_messages));
|
||||
assert(result.output);
|
||||
assert(result.curr_test_messages);
|
||||
result.output->next = result.output->prev = result.output;
|
||||
result.curr_test_messages->next = result.curr_test_messages->prev = result.curr_test_messages;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_Deinit(DN_UTCore *ut)
|
||||
{
|
||||
for (DN_UTStr8Link *it = ut->output->next; it != ut->output; it = ut->output->next) {
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
free(it);
|
||||
}
|
||||
free(ut->output);
|
||||
|
||||
for (DN_UTStr8Link *it = ut->curr_test_messages->next; it != ut->curr_test_messages; it = ut->curr_test_messages->next) {
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
free(it);
|
||||
}
|
||||
free(ut->curr_test_messages);
|
||||
}
|
||||
|
||||
void DN_UT_BeginFV(DN_UTCore *ut, char const *fmt, va_list args)
|
||||
{
|
||||
assert(ut->output && ut->curr_test_messages && "Test must be initialised by calling DN_UT_Init()");
|
||||
assert(ut->state == DN_UTState_Nil &&
|
||||
"Nesting a unit ut within another unit test is not allowed, ensure"
|
||||
"the first test has finished by calling DN_UT_End");
|
||||
|
||||
ut->num_tests_in_group++;
|
||||
ut->state = DN_UTState_TestBegun;
|
||||
ut->name_size = 0;
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
ut->name_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
assert(ut->name_size < sizeof(ut->name));
|
||||
vsnprintf(ut->name, sizeof(ut->name), fmt, args);
|
||||
}
|
||||
|
||||
void DN_UT_BeginF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UT_BeginFV(ut, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static DN_UTStr8Link *DN_UT_AllocStr8LinkFV(char const *fmt, va_list args)
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
size_t size = vsnprintf(nullptr, 0, fmt, args_copy) + 1;
|
||||
va_end(args_copy);
|
||||
|
||||
DN_UTStr8Link *result = (DN_UTStr8Link *)malloc(sizeof(*result) + size);
|
||||
if (result) {
|
||||
result->data = (char *)result + sizeof(*result);
|
||||
result->size = vsnprintf(result->data, size, fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_End(DN_UTCore *ut)
|
||||
{
|
||||
assert(ut->state != DN_UTState_Nil && "Test was marked as ended but a ut was never commenced using DN_UT_Begin");
|
||||
size_t pad_size = DN_UT_RESULT_LPAD - (DN_UT_SPACING + ut->name_size);
|
||||
if (pad_size < 0)
|
||||
pad_size = 0;
|
||||
|
||||
char pad_buffer[DN_UT_RESULT_LPAD] = {};
|
||||
memset(pad_buffer, DN_UT_RESULT_PAD_CHAR, pad_size);
|
||||
|
||||
DN_UT_LogF(ut, "%*s%.*s%.*s", DN_UT_SPACING, "", (int)ut->name_size, ut->name, (int)pad_size, pad_buffer);
|
||||
if (ut->state == DN_UTState_TestFailed) {
|
||||
DN_UT_LogF(ut, DN_UT_BAD_COLOR " FAILED");
|
||||
} else {
|
||||
DN_UT_LogF(ut, DN_UT_GOOD_COLOR " OK");
|
||||
ut->num_tests_ok_in_group++;
|
||||
}
|
||||
DN_UT_LogF(ut, DN_UT_COLOR_RESET "\n");
|
||||
ut->state = DN_UTState_Nil;
|
||||
|
||||
// NOTE: Append any test messages (like assertions) into the main output buffer
|
||||
for (DN_UTStr8Link *it = ut->curr_test_messages->next; it != ut->curr_test_messages; it = ut->curr_test_messages->next) {
|
||||
// NOTE: Detach
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
|
||||
// NOTE: Attach
|
||||
it->next = ut->output;
|
||||
it->prev = ut->output->prev;
|
||||
it->next->prev = it;
|
||||
it->prev->next = it;
|
||||
}
|
||||
}
|
||||
|
||||
void DN_UT_LogF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
assert(ut->output && ut->curr_test_messages && "UT was not initialised by calling UT_Init yet");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UTStr8Link *result = DN_UT_AllocStr8LinkFV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
result->next = ut->output;
|
||||
result->prev = ut->output->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
}
|
||||
|
||||
void DN_UT_LogInsideTestF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
assert(ut->state >= DN_UTState_TestBegun && "");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UTStr8Link *result = DN_UT_AllocStr8LinkFV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
result->next = ut->curr_test_messages;
|
||||
result->prev = ut->curr_test_messages->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
}
|
||||
|
||||
bool DN_UT_AllTestsPassed(DN_UTCore const *ut)
|
||||
{
|
||||
bool result = ut->num_tests_ok_in_group == ut->num_tests_in_group;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_PrintTests(DN_UTCore const *ut)
|
||||
{
|
||||
for (DN_UTStr8Link *it = ut->output->next; it != ut->output; it = it->next)
|
||||
fprintf(stdout, "%.*s", (int)it->size, it->data);
|
||||
|
||||
bool all_clear = DN_UT_AllTestsPassed(ut);
|
||||
fprintf(stdout,
|
||||
"%s\n %02d/%02d tests passed -- %s\n\n" DN_UT_COLOR_RESET,
|
||||
all_clear ? DN_UT_GOOD_COLOR : DN_UT_BAD_COLOR,
|
||||
ut->num_tests_ok_in_group,
|
||||
ut->num_tests_in_group,
|
||||
all_clear ? "OK" : "FAILED");
|
||||
}
|
||||
#endif // DN_UT_IMPLEMENTATION
|
@ -1,636 +0,0 @@
|
||||
#if !defined(DN_KECCAK_H)
|
||||
#define DN_KECCAK_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ | $$ |$$ _____|$$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ |
|
||||
// $$ |$$ / $$ | $$ / \__|$$ / \__|$$ / $$ |$$ |$$ /
|
||||
// $$$$$ / $$$$$\ $$ | $$ | $$$$$$$$ |$$$$$ /
|
||||
// $$ $$< $$ __| $$ | $$ | $$ __$$ |$$ $$<
|
||||
// $$ |\$$\ $$ | $$ | $$\ $$ | $$\ $$ | $$ |$$ |\$$\
|
||||
// $$ | \$$\ $$$$$$$$\ \$$$$$$ |\$$$$$$ |$$ | $$ |$$ | \$$\
|
||||
// \__| \__|\________| \______/ \______/ \__| \__|\__| \__|
|
||||
//
|
||||
// dn_keccak.h -- FIPS202 SHA3 + non-finalized SHA3 (aka. Keccak) hashing algorithms
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Implementation of the Keccak hashing algorithms from the Keccak and SHA3
|
||||
// families (including the FIPS202 published algorithms and the non-finalized
|
||||
// ones, i.e. the ones used in Ethereum and Monero which adopted SHA3 before it
|
||||
// was finalized. The only difference between the 2 is a different delimited
|
||||
// suffix).
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2021 github.com/doy-lee
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
|
||||
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
|
||||
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \______| \______/ \__| \__| \______/
|
||||
//
|
||||
// Options -- Compile time build customisation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// - Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file.
|
||||
//
|
||||
// #define DN_KECCAK_IMPLEMENTATION
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if !defined(DN_KECCAK_MEMCPY)
|
||||
#include <string.h>
|
||||
#define DN_KECCAK_MEMCPY(dest, src, count) memcpy(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KECCAK_MEMCMP)
|
||||
#include <string.h>
|
||||
#define DN_KECCAK_MEMCMP(dest, src, count) memcmp(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KECCAK_MEMSET)
|
||||
#include <string.h>
|
||||
#define DN_KECCAK_MEMSET(dest, byte, count) memset(dest, byte, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KECCAK_ASSERT)
|
||||
#if defined(NDEBUG)
|
||||
#define DN_KECCAK_ASSERT(expr)
|
||||
#else
|
||||
#define DN_KECCAK_ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
(*(volatile int *)0) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Use this macro in a printf-like function,
|
||||
/*
|
||||
DN_KeccakString64 string = {};
|
||||
printf("%.*s\n", DN_KECCAK_STRING64_FMT(string));
|
||||
*/
|
||||
#define DN_KECCAK_STRING56_FMT(string) 56, string
|
||||
#define DN_KECCAK_STRING64_FMT(string) 64, string
|
||||
#define DN_KECCAK_STRING96_FMT(string) 96, string
|
||||
#define DN_KECCAK_STRING128_FMT(string) 128, string
|
||||
|
||||
typedef unsigned char DN_KeccakU8;
|
||||
typedef unsigned short DN_KeccakU16;
|
||||
typedef unsigned int DN_KeccakU32;
|
||||
typedef unsigned int DN_KeccakUint;
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int64 DN_KeccakU64;
|
||||
#else
|
||||
typedef unsigned long long DN_KeccakU64;
|
||||
#endif
|
||||
|
||||
typedef struct DN_KeccakBytes28 { char data[28]; } DN_KeccakBytes28; // 224 bit
|
||||
typedef struct DN_KeccakBytes32 { char data[32]; } DN_KeccakBytes32; // 256 bit
|
||||
typedef struct DN_KeccakBytes48 { char data[48]; } DN_KeccakBytes48; // 384 bit
|
||||
typedef struct DN_KeccakBytes64 { char data[64]; } DN_KeccakBytes64; // 512 bit
|
||||
|
||||
typedef struct DN_KeccakString56 { char data[(sizeof(DN_KeccakBytes28) * 2) + 1]; } DN_KeccakString56;
|
||||
typedef struct DN_KeccakString64 { char data[(sizeof(DN_KeccakBytes32) * 2) + 1]; } DN_KeccakString64;
|
||||
typedef struct DN_KeccakString96 { char data[(sizeof(DN_KeccakBytes48) * 2) + 1]; } DN_KeccakString96;
|
||||
typedef struct DN_KeccakString128 { char data[(sizeof(DN_KeccakBytes64) * 2) + 1]; } DN_KeccakString128;
|
||||
|
||||
#define DN_KECCAK_LANE_SIZE_U64 5
|
||||
typedef struct DN_KeccakState
|
||||
{
|
||||
DN_KeccakU8 state[DN_KECCAK_LANE_SIZE_U64 * DN_KECCAK_LANE_SIZE_U64 * sizeof(DN_KeccakU64)];
|
||||
int state_size; // The number of bytes written to the state
|
||||
int absorb_size; // The amount of bytes to absorb/sponge in/from the state
|
||||
int hash_size_bits; // The size of the hash the context was initialised for in bits
|
||||
char delimited_suffix; // The delimited suffix of the current hash
|
||||
} DN_KeccakState;
|
||||
|
||||
// NOTE: SHA3/Keccak Streaming API /////////////////////////////////////////////////////////////////
|
||||
// Setup a hashing state for either
|
||||
// - FIPS 202 SHA3
|
||||
// - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3)
|
||||
// The non-finalized SHA3 version is the one adopted by many cryptocurrencies
|
||||
// such as Ethereum and Monero as they adopted SHA3 before it was finalized.
|
||||
//
|
||||
// The state produced from this function is to be used alongside the
|
||||
// 'KeccakUpdate' and 'KeccakFinish' functions.
|
||||
//
|
||||
// sha3: If true, setup the state for FIPS 202 SHA3, otherwise the non-finalized version
|
||||
// hash_size_bits: The number of bits to setup the context for, available sizes are 224, 256, 384 and 512.
|
||||
DN_KeccakState DN_KeccakSHA3Init(bool sha3, int hash_size_bits);
|
||||
|
||||
// After initialising a 'DN_KeccakState' via 'DN_KeccakSHA3Init', iteratively
|
||||
// update the hash with new data by calling 'DN_KeccakUpdate'. On completion,
|
||||
// call 'DN_KeccakFinish' to generate the hash from the state. The 'dest_size'
|
||||
// must be at-least the (bit-size / 8), i.e. for 'DN_Keccak512Init' it must be
|
||||
// atleast (512 / 8) bytes, 64 bytes.
|
||||
void DN_KeccakUpdate(DN_KeccakState *keccak, void const *data, DN_KeccakU64 data_size);
|
||||
void DN_KeccakFinish(DN_KeccakState *keccak, void *dest, int dest_size);
|
||||
|
||||
// NOTE: Simple API ////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper function that combines the Init, Update and Finish step in one shot,
|
||||
// i.e. hashing a singlular contiguous buffer. Use the streaming API if data
|
||||
// is split across different buffers.
|
||||
void DN_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, DN_KeccakU64 src_size, void *dest, int dest_size);
|
||||
|
||||
#define DN_SHA3Hash(hash_size_bits, src, src_size, dest, dest_size) DN_KeccakSHA3Hash(true /*sha3*/, hash_size_bits, src, src_size, dest, dest_size)
|
||||
#define DN_SHA3_224(src, src_size, dest, dest_size) DN_SHA3Hash(224, src, src_size, dest, dest_size)
|
||||
#define DN_SHA3_256(src, src_size, dest, dest_size) DN_SHA3Hash(256, src, src_size, dest, dest_size)
|
||||
#define DN_SHA3_384(src, src_size, dest, dest_size) DN_SHA3Hash(384, src, src_size, dest, dest_size)
|
||||
#define DN_SHA3_512(src, src_size, dest, dest_size) DN_SHA3Hash(512, src, src_size, dest, dest_size)
|
||||
|
||||
#define DN_KeccakHash(hash_size_bits, src, src_size, dest, dest_size) DN_KeccakSHA3Hash(false /*sha3*/, hash_size_bits, src, src_size, dest, dest_size)
|
||||
#define DN_Keccak224(src, src_size, dest, dest_size) DN_KeccakHash(224, src, src_size, dest, dest_size)
|
||||
#define DN_Keccak256(src, src_size, dest, dest_size) DN_KeccakHash(256, src, src_size, dest, dest_size)
|
||||
#define DN_Keccak384(src, src_size, dest, dest_size) DN_KeccakHash(384, src, src_size, dest, dest_size)
|
||||
#define DN_Keccak512(src, src_size, dest, dest_size) DN_KeccakHash(512, src, src_size, dest, dest_size)
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_SHA3_224ToBytes28(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes32 DN_SHA3_256ToBytes32(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes48 DN_SHA3_384ToBytes48(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes64 DN_SHA3_512ToBytes64(void *bytes, DN_KeccakU64 bytes_size);
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_Keccak224ToBytes28(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes32 DN_Keccak256ToBytes32(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes48 DN_Keccak384ToBytes48(void *bytes, DN_KeccakU64 bytes_size);
|
||||
DN_KeccakBytes64 DN_Keccak512ToBytes64(void *bytes, DN_KeccakU64 bytes_size);
|
||||
|
||||
#if defined(DN_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_SHA3_224StringToBytes28(DN_Str8 string);
|
||||
DN_KeccakBytes32 DN_SHA3_256StringToBytes32(DN_Str8 string);
|
||||
DN_KeccakBytes48 DN_SHA3_384StringToBytes48(DN_Str8 string);
|
||||
DN_KeccakBytes64 DN_SHA3_512StringToBytes64(DN_Str8 string);
|
||||
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_Keccak224StringToBytes28(DN_Str8 string);
|
||||
DN_KeccakBytes32 DN_Keccak256StringToBytes32(DN_Str8 string);
|
||||
DN_KeccakBytes48 DN_Keccak384StringToBytes48(DN_Str8 string);
|
||||
DN_KeccakBytes64 DN_Keccak512StringToBytes64(DN_Str8 string);
|
||||
#endif // DN_H
|
||||
|
||||
// NOTE: Helper functions //////////////////////////////////////////////////////////////////////////
|
||||
// Convert a binary buffer into its hex representation into dest. The dest
|
||||
// buffer must be large enough to contain the hex representation, i.e.
|
||||
// atleast src_size * 2). This function does *not* null-terminate the buffer.
|
||||
void DN_KeccakBytesToHex(void const *src, DN_KeccakU64 src_size, char *dest, DN_KeccakU64 dest_size);
|
||||
|
||||
// Converts a fixed amount of bytes into a hexadecimal string. Helper functions
|
||||
// that call into DN_KeccakBytesToHex.
|
||||
// return: The hexadecimal string of the bytes, null-terminated.
|
||||
DN_KeccakString56 DN_KeccakBytes28ToHex(DN_KeccakBytes28 const *bytes);
|
||||
DN_KeccakString64 DN_KeccakBytes32ToHex(DN_KeccakBytes32 const *bytes);
|
||||
DN_KeccakString96 DN_KeccakBytes48ToHex(DN_KeccakBytes48 const *bytes);
|
||||
DN_KeccakString128 DN_KeccakBytes64ToHex(DN_KeccakBytes64 const *bytes);
|
||||
|
||||
// Compares byte data structures for byte equality (via memcmp).
|
||||
// return: 1 if the contents are equal otherwise 0.
|
||||
int DN_KeccakBytes28Equals(DN_KeccakBytes28 const *a, DN_KeccakBytes28 const *b);
|
||||
int DN_KeccakBytes32Equals(DN_KeccakBytes32 const *a, DN_KeccakBytes32 const *b);
|
||||
int DN_KeccakBytes48Equals(DN_KeccakBytes48 const *a, DN_KeccakBytes48 const *b);
|
||||
int DN_KeccakBytes64Equals(DN_KeccakBytes64 const *a, DN_KeccakBytes64 const *b);
|
||||
|
||||
#if defined(DN_H) && defined(DN_WITH_HEX)
|
||||
// NOTE: Other helper functions for DN data structures ////////////////////////////////////////////
|
||||
// Converts a 64 character hex string into the 32 byte binary representation.
|
||||
// Invalid hex characters in the string will be represented as 0.
|
||||
// hex: Must be exactly a 64 character hex string.
|
||||
DN_KeccakBytes32 DN_KeccakHex64StringToBytes(DN_Str8 hex);
|
||||
#endif // DN_H && DN_WITH_HEX
|
||||
#endif // DN_KECCAK_H
|
||||
|
||||
#if defined(DN_KECCAK_IMPLEMENTATION)
|
||||
DN_KeccakU64 const DN_KECCAK_ROUNDS[] = {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B,
|
||||
0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088,
|
||||
0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
};
|
||||
|
||||
DN_KeccakU64 const DN_KECCAK_ROTATIONS[][5] =
|
||||
{
|
||||
{0, 36, 3, 41, 18},
|
||||
{1, 44, 10, 45, 2},
|
||||
{62, 6, 43, 15, 61},
|
||||
{28, 55, 25, 21, 56},
|
||||
{27, 20, 39, 8, 14},
|
||||
};
|
||||
|
||||
#define DN_KECCAK_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate)))))
|
||||
|
||||
static void DN_Keccak__Permute(void *state)
|
||||
{
|
||||
// TODO(dn): Do some profiling on unrolling and can we SIMD some part of
|
||||
// this? Unroll loop, look at data dependencies and investigate.
|
||||
|
||||
DN_KeccakU64 *lanes_u64 = (DN_KeccakU64 *)state;
|
||||
for (int round_index = 0; round_index < 24; round_index++)
|
||||
{
|
||||
#define LANE_INDEX(x, y) ((x) + ((y) * DN_KECCAK_LANE_SIZE_U64))
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
#if 1
|
||||
DN_KeccakU64 c[DN_KECCAK_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KECCAK_LANE_SIZE_U64; x++)
|
||||
{
|
||||
c[x] = lanes_u64[LANE_INDEX(x, 0)] ^
|
||||
lanes_u64[LANE_INDEX(x, 1)] ^
|
||||
lanes_u64[LANE_INDEX(x, 2)] ^
|
||||
lanes_u64[LANE_INDEX(x, 3)] ^
|
||||
lanes_u64[LANE_INDEX(x, 4)];
|
||||
}
|
||||
|
||||
DN_KeccakU64 d[DN_KECCAK_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KECCAK_LANE_SIZE_U64; x++)
|
||||
d[x] = c[(x + 4) % DN_KECCAK_LANE_SIZE_U64] ^ DN_KECCAK_ROL64(c[(x + 1) % DN_KECCAK_LANE_SIZE_U64], 1);
|
||||
|
||||
for (int y = 0; y < DN_KECCAK_LANE_SIZE_U64; y++)
|
||||
for (int x = 0; x < DN_KECCAK_LANE_SIZE_U64; x++)
|
||||
lanes_u64[LANE_INDEX(x, y)] ^= d[x];
|
||||
#else
|
||||
DN_KeccakU64 c[5], d[5];
|
||||
c[0] = lanes_u64[0 * 5 + 0] ^ lanes_u64[1 * 5 + 0] ^ lanes_u64[2 * 5 + 0] ^ lanes_u64[3 * 5 + 0] ^ lanes_u64[4 * 5 + 0];
|
||||
c[1] = lanes_u64[0 * 5 + 1] ^ lanes_u64[1 * 5 + 1] ^ lanes_u64[2 * 5 + 1] ^ lanes_u64[3 * 5 + 1] ^ lanes_u64[4 * 5 + 1];
|
||||
c[2] = lanes_u64[0 * 5 + 2] ^ lanes_u64[1 * 5 + 2] ^ lanes_u64[2 * 5 + 2] ^ lanes_u64[3 * 5 + 2] ^ lanes_u64[4 * 5 + 2];
|
||||
c[3] = lanes_u64[0 * 5 + 3] ^ lanes_u64[1 * 5 + 3] ^ lanes_u64[2 * 5 + 3] ^ lanes_u64[3 * 5 + 3] ^ lanes_u64[4 * 5 + 3];
|
||||
c[4] = lanes_u64[0 * 5 + 4] ^ lanes_u64[1 * 5 + 4] ^ lanes_u64[2 * 5 + 4] ^ lanes_u64[3 * 5 + 4] ^ lanes_u64[4 * 5 + 4];
|
||||
|
||||
d[0] = c[4] ^ DN_KECCAK_ROL64(c[1], 1);
|
||||
d[1] = c[0] ^ DN_KECCAK_ROL64(c[2], 1);
|
||||
d[2] = c[1] ^ DN_KECCAK_ROL64(c[3], 1);
|
||||
d[3] = c[2] ^ DN_KECCAK_ROL64(c[4], 1);
|
||||
d[4] = c[3] ^ DN_KECCAK_ROL64(c[0], 1);
|
||||
#endif
|
||||
|
||||
// ?? and ?? steps ///////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakU64 b[DN_KECCAK_LANE_SIZE_U64 * DN_KECCAK_LANE_SIZE_U64];
|
||||
for (int y = 0; y < DN_KECCAK_LANE_SIZE_U64; y++)
|
||||
{
|
||||
for (int x = 0; x < DN_KECCAK_LANE_SIZE_U64; x++)
|
||||
{
|
||||
DN_KeccakU64 lane = lanes_u64[LANE_INDEX(x, y)];
|
||||
DN_KeccakU64 rotate_count = DN_KECCAK_ROTATIONS[x][y];
|
||||
b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DN_KECCAK_ROL64(lane, rotate_count);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
for (int y = 0; y < DN_KECCAK_LANE_SIZE_U64; y++)
|
||||
{
|
||||
for (int x = 0; x < DN_KECCAK_LANE_SIZE_U64; x++)
|
||||
{
|
||||
DN_KeccakU64 rhs = ~b[LANE_INDEX((x + 1) % 5, y)] &
|
||||
b[LANE_INDEX((x + 2) % 5, y)];
|
||||
|
||||
lanes_u64[LANE_INDEX(x, y)] = b[LANE_INDEX(x, y)] ^ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
lanes_u64[LANE_INDEX(0, 0)] ^= DN_KECCAK_ROUNDS[round_index];
|
||||
#undef LANE_INDEX
|
||||
#undef DN_KECCAK_ROL64
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Streaming API /////////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakState DN_KeccakSHA3Init(bool sha3, int hash_size_bits)
|
||||
{
|
||||
char const SHA3_DELIMITED_SUFFIX = 0x06;
|
||||
char const KECCAK_DELIMITED_SUFFIX = 0x01;
|
||||
int const bitrate = 1600 - (hash_size_bits * 2);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DN_KeccakState result = {};
|
||||
#else
|
||||
DN_KeccakState result = {0};
|
||||
#endif
|
||||
result.hash_size_bits = hash_size_bits;
|
||||
result.absorb_size = bitrate / 8;
|
||||
result.delimited_suffix = sha3 ? SHA3_DELIMITED_SUFFIX : KECCAK_DELIMITED_SUFFIX;
|
||||
DN_KECCAK_ASSERT(bitrate + (hash_size_bits * 2) /*capacity*/ == 1600);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_KeccakUpdate(DN_KeccakState *keccak, void const *data, DN_KeccakU64 data_size)
|
||||
{
|
||||
DN_KeccakU8 *state = keccak->state;
|
||||
DN_KeccakU8 const *ptr = (DN_KeccakU8 *)data;
|
||||
DN_KeccakU64 ptr_size = data_size;
|
||||
while (ptr_size > 0)
|
||||
{
|
||||
DN_KeccakU64 space = keccak->absorb_size - keccak->state_size;
|
||||
int bytes_to_absorb = (int)(space < ptr_size ? space : ptr_size);
|
||||
|
||||
for (int index = 0; index < bytes_to_absorb; index++)
|
||||
state[keccak->state_size + index] ^= ptr[index];
|
||||
|
||||
ptr += bytes_to_absorb;
|
||||
keccak->state_size += bytes_to_absorb;
|
||||
ptr_size -= bytes_to_absorb;
|
||||
|
||||
if (keccak->state_size >= keccak->absorb_size)
|
||||
{
|
||||
DN_KECCAK_ASSERT(keccak->state_size == keccak->absorb_size);
|
||||
DN_Keccak__Permute(state);
|
||||
keccak->state_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DN_KeccakFinish(DN_KeccakState *keccak, void *dest, int dest_size)
|
||||
{
|
||||
DN_KECCAK_ASSERT(dest_size >= keccak->hash_size_bits / 8);
|
||||
|
||||
// Sponge Finalization Step: Final padding bit /////////////////////////////////////////////////
|
||||
int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1;
|
||||
int const delimited_suffix_index = keccak->state_size;
|
||||
DN_KECCAK_ASSERT(delimited_suffix_index < keccak->absorb_size);
|
||||
|
||||
DN_KeccakU8 *state = keccak->state;
|
||||
state[delimited_suffix_index] ^= keccak->delimited_suffix;
|
||||
|
||||
// NOTE: In the reference implementation, it checks that if the
|
||||
// delimited suffix is set to the padding bit (0x80), then we need to
|
||||
// permute twice. Once for the delimited suffix, and a second time for
|
||||
// the "padding" permute.
|
||||
//
|
||||
// However all standard algorithms either specify a 0x01, or 0x06, 0x04
|
||||
// delimited suffix and so forth- so this case is never hit. We can omit
|
||||
// this from the implementation here.
|
||||
|
||||
state[INDEX_OF_0X80_BYTE] ^= 0x80;
|
||||
DN_Keccak__Permute(state);
|
||||
|
||||
// Squeeze Step: Squeeze bytes from the state into our hash ////////////////////////////////////
|
||||
DN_KeccakU8 *dest_u8 = (DN_KeccakU8 *)dest;
|
||||
int const squeeze_count = dest_size / keccak->absorb_size;
|
||||
int squeeze_index = 0;
|
||||
for (; squeeze_index < squeeze_count; squeeze_index++)
|
||||
{
|
||||
if (squeeze_index) DN_Keccak__Permute(state);
|
||||
DN_KECCAK_MEMCPY(dest_u8, state, keccak->absorb_size);
|
||||
dest_u8 += keccak->absorb_size;
|
||||
}
|
||||
|
||||
// Squeeze Finalisation Step: Remainder bytes in hash //////////////////////////////////////////
|
||||
int const remainder = dest_size % keccak->absorb_size;
|
||||
if (remainder)
|
||||
{
|
||||
if (squeeze_index) DN_Keccak__Permute(state);
|
||||
DN_KECCAK_MEMCPY(dest_u8, state, remainder);
|
||||
}
|
||||
}
|
||||
|
||||
void DN_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, DN_KeccakU64 src_size, void *dest, int dest_size)
|
||||
{
|
||||
DN_KeccakState keccak = DN_KeccakSHA3Init(sha3, hash_size_bits);
|
||||
DN_KeccakUpdate(&keccak, src, src_size);
|
||||
DN_KeccakFinish(&keccak, dest, dest_size);
|
||||
}
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_SHA3_224ToBytes28(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes28 result;
|
||||
DN_SHA3_224(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes32 DN_SHA3_256ToBytes32(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes32 result;
|
||||
DN_SHA3_256(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes48 DN_SHA3_384ToBytes48(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes48 result;
|
||||
DN_SHA3_384(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes64 DN_SHA3_512ToBytes64(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes64 result;
|
||||
DN_SHA3_512(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_Keccak224ToBytes28(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes28 result;
|
||||
DN_Keccak224(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes32 DN_Keccak256ToBytes32(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes32 result;
|
||||
DN_Keccak256(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes48 DN_Keccak384ToBytes48(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes48 result;
|
||||
DN_Keccak384(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes64 DN_Keccak512ToBytes64(void *bytes, DN_KeccakU64 bytes_size)
|
||||
{
|
||||
DN_KeccakBytes64 result;
|
||||
DN_Keccak512(bytes, bytes_size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#if defined(DN_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_SHA3_224StringToBytes28(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes28 result;
|
||||
DN_SHA3_224(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes32 DN_SHA3_256StringToBytes32(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes32 result;
|
||||
DN_SHA3_256(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes48 DN_SHA3_384StringToBytes48(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes48 result;
|
||||
DN_SHA3_384(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes64 DN_SHA3_512StringToBytes64(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes64 result;
|
||||
DN_SHA3_512(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_H
|
||||
|
||||
#if defined(DN_H)
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KeccakBytes28 DN_Keccak224StringToBytes28(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes28 result;
|
||||
DN_Keccak224(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes32 DN_Keccak256StringToBytes32(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes32 result;
|
||||
DN_Keccak256(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes48 DN_Keccak384StringToBytes48(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes48 result;
|
||||
DN_Keccak384(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakBytes64 DN_Keccak512StringToBytes64(DN_Str8 string)
|
||||
{
|
||||
DN_KeccakBytes64 result;
|
||||
DN_Keccak512(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_H
|
||||
|
||||
// NOTE: Helper functions //////////////////////////////////////////////////////////////////////////
|
||||
void DN_KeccakBytesToHex(void const *src, DN_KeccakU64 src_size, char *dest, DN_KeccakU64 dest_size)
|
||||
{
|
||||
(void)src_size; (void)dest_size;
|
||||
DN_KECCAK_ASSERT(dest_size >= src_size * 2);
|
||||
|
||||
unsigned char *src_u8 = (unsigned char *)src;
|
||||
for (DN_KeccakU64 src_index = 0, dest_index = 0;
|
||||
src_index < src_size;
|
||||
src_index += 1, dest_index += 2)
|
||||
{
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index + 0] = hex01 < 10 ? (hex01 + '0') : (hex01 - 10) + 'a';
|
||||
dest[dest_index + 1] = hex02 < 10 ? (hex02 + '0') : (hex02 - 10) + 'a';
|
||||
}
|
||||
}
|
||||
|
||||
DN_KeccakString56 DN_KeccakBytes28ToHex(DN_KeccakBytes28 const *bytes)
|
||||
{
|
||||
DN_KeccakString56 result;
|
||||
DN_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakString64 DN_KeccakBytes32ToHex(DN_KeccakBytes32 const *bytes)
|
||||
{
|
||||
DN_KeccakString64 result;
|
||||
DN_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakString96 DN_KeccakBytes48ToHex(DN_KeccakBytes48 const *bytes)
|
||||
{
|
||||
DN_KeccakString96 result;
|
||||
DN_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KeccakString128 DN_KeccakBytes64ToHex(DN_KeccakBytes64 const *bytes)
|
||||
{
|
||||
DN_KeccakString128 result;
|
||||
DN_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KeccakBytes28Equals(DN_KeccakBytes28 const *a, DN_KeccakBytes28 const *b)
|
||||
{
|
||||
int result = DN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KeccakBytes32Equals(DN_KeccakBytes32 const *a, DN_KeccakBytes32 const *b)
|
||||
{
|
||||
int result = DN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KeccakBytes48Equals(DN_KeccakBytes48 const *a, DN_KeccakBytes48 const *b)
|
||||
{
|
||||
int result = DN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KeccakBytes64Equals(DN_KeccakBytes64 const *a, DN_KeccakBytes64 const *b)
|
||||
{
|
||||
int result = DN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_H) && defined(DN_WITH_HEX)
|
||||
// NOTE: Other helper functions for DN data structures ////////////////////////////////////////////
|
||||
DN_KeccakBytes32 DN_KeccakHex64StringToBytes(DN_Str8 hex)
|
||||
{
|
||||
DN_KECCAK_ASSERT(hex.size == 64);
|
||||
DN_KeccakBytes32 result;
|
||||
DN_Hex_CString8ToByteBuffer(hex.data, hex.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_H && DN_WITH_HEX
|
||||
#endif // DN_KECCAK_IMPLEMENTATION
|
@ -1,230 +0,0 @@
|
||||
#if !defined(DN_UTEST_H)
|
||||
#define DN_UTEST_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\
|
||||
// $$ | $$ |\__$$ __|$$ _____|$$ __$$\\__$$ __|
|
||||
// $$ | $$ | $$ | $$ | $$ / \__| $$ |
|
||||
// $$ | $$ | $$ | $$$$$\ \$$$$$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ __| \____$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ | $$\ $$ | $$ |
|
||||
// \$$$$$$ | $$ | $$$$$$$$\ \$$$$$$ | $$ |
|
||||
// \______/ \__| \________| \______/ \__|
|
||||
//
|
||||
// dn_utest.h -- Extremely minimal unit testing framework
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A super minimal testing framework, most of the logic here is the pretty
|
||||
// printing of test results.
|
||||
//
|
||||
// NOTE: Configuration /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// #define DN_UTEST_IMPLEMENTATION
|
||||
// Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file. This will also automatically enable the JSMN
|
||||
// implementation.
|
||||
//
|
||||
// #define DN_UTEST_RESULT_LPAD
|
||||
// Define this to a number to specify how much to pad the output of the test
|
||||
// result line before the test result is printed.
|
||||
//
|
||||
// #define DN_UTEST_RESULT_PAD_CHAR
|
||||
// Define this to a character to specify the default character to use for
|
||||
// padding. By default this is '.'
|
||||
//
|
||||
// #define DN_UTEST_SPACING
|
||||
// Define this to a number to specify the number of spaces between the group
|
||||
// declaration and the test output in the group.
|
||||
//
|
||||
// #define DN_UTEST_BAD_COLOR
|
||||
// Define this to a terminal color code to specify what color errors will be
|
||||
// presented as.
|
||||
//
|
||||
// #define DN_UTEST_GOOD_COLOR
|
||||
// Define this to a terminal color code to specify what color sucess will be
|
||||
// presented as.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(DN_UTEST_RESULT_LPAD)
|
||||
#define DN_UTEST_RESULT_LPAD 90
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UTEST_RESULT_PAD_CHAR)
|
||||
#define DN_UTEST_RESULT_PAD_CHAR '.'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UTEST_SPACING)
|
||||
#define DN_UTEST_SPACING 2
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UTEST_BAD_COLOR)
|
||||
#define DN_UTEST_BAD_COLOR "\x1b[31m"
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UTEST_GOOD_COLOR)
|
||||
#define DN_UTEST_GOOD_COLOR "\x1b[32m"
|
||||
#endif
|
||||
|
||||
#define DN_UTEST_COLOR_RESET "\x1b[0m"
|
||||
|
||||
#define DN_UTEST_GROUP(test, fmt, ...) \
|
||||
for (DN_UTest *test_var_ = (printf(fmt "\n", ## __VA_ARGS__), &test); \
|
||||
test_var_ != nullptr; \
|
||||
DN_UTest_PrintStats(&test), test_var_ = nullptr)
|
||||
|
||||
#define DN_UTEST_TEST(fmt, ...) \
|
||||
for (int dummy_ = (DN_UTest_Begin(test_var_, fmt, ## __VA_ARGS__), 0); \
|
||||
(void)dummy_, test_var_->state == DN_UTestState_TestBegun; \
|
||||
DN_UTest_End(test_var_))
|
||||
|
||||
#define DN_UTEST_ASSERTF(test, expr, fmt, ...) \
|
||||
DN_UTEST_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DN_UTEST_ASSERT(test, expr) \
|
||||
DN_UTEST_ASSERT_AT((test), __FILE__, __LINE__, (expr))
|
||||
|
||||
// TODO: Fix the logs. They print before the tests, we should accumulate logs
|
||||
// per test, then, dump them on test on. But to do this nicely without crappy
|
||||
// mem management we need to implement an arena.
|
||||
#define DN_UTEST_LOG(fmt, ...) \
|
||||
fprintf(stdout, "%*s" fmt "\n", DN_UTEST_SPACING * 2, "", ##__VA_ARGS__)
|
||||
|
||||
#define DN_UTEST_ASSERTF_AT(test, file, line, expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTestState_TestFailed; \
|
||||
fprintf(stderr, \
|
||||
"%*sAssertion Triggered\n" \
|
||||
"%*sFile: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n" \
|
||||
"%*sReason: " fmt "\n\n", \
|
||||
DN_UTEST_SPACING * 2, \
|
||||
"", \
|
||||
DN_UTEST_SPACING * 3, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UTEST_SPACING * 3, \
|
||||
"", \
|
||||
DN_UTEST_SPACING * 3, \
|
||||
"", \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_UTEST_ASSERT_AT(test, file, line, expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTestState_TestFailed; \
|
||||
fprintf(stderr, \
|
||||
"%*sFile: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n\n", \
|
||||
DN_UTEST_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UTEST_SPACING * 2, \
|
||||
""); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Header ////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum DN_UTestState {
|
||||
DN_UTestState_Nil,
|
||||
DN_UTestState_TestBegun,
|
||||
DN_UTestState_TestFailed,
|
||||
} DN_UTestState;
|
||||
|
||||
typedef struct DN_UTest {
|
||||
int num_tests_in_group;
|
||||
int num_tests_ok_in_group;
|
||||
DN_UTestState state;
|
||||
bool finished;
|
||||
char name[256];
|
||||
size_t name_size;
|
||||
} DN_UTest;
|
||||
|
||||
void DN_UTest_PrintStats(DN_UTest *test);
|
||||
void DN_UTest_BeginV(DN_UTest *test, char const *fmt, va_list args);
|
||||
void DN_UTest_Begin(DN_UTest *test, char const *fmt, ...);
|
||||
void DN_UTest_End(DN_UTest *test);
|
||||
#endif // DN_UTEST_H
|
||||
|
||||
// NOTE: Implementation ////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_UTEST_IMPLEMENTATION)
|
||||
void DN_UTest_PrintStats(DN_UTest *test)
|
||||
{
|
||||
if (test->finished)
|
||||
return;
|
||||
|
||||
test->finished = true;
|
||||
bool all_clear = test->num_tests_ok_in_group == test->num_tests_in_group;
|
||||
fprintf(stdout,
|
||||
"%s\n %02d/%02d tests passed -- %s\n\n" DN_UTEST_COLOR_RESET,
|
||||
all_clear ? DN_UTEST_GOOD_COLOR : DN_UTEST_BAD_COLOR,
|
||||
test->num_tests_ok_in_group,
|
||||
test->num_tests_in_group,
|
||||
all_clear ? "OK" : "FAILED");
|
||||
}
|
||||
|
||||
void DN_UTest_BeginV(DN_UTest *test, char const *fmt, va_list args)
|
||||
{
|
||||
assert(test->state == DN_UTestState_Nil &&
|
||||
"Nesting a unit test within another unit test is not allowed, ensure"
|
||||
"the first test has finished by calling DN_UTest_End");
|
||||
|
||||
test->num_tests_in_group++;
|
||||
test->state = DN_UTestState_TestBegun;
|
||||
|
||||
test->name_size = 0;
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
test->name_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
assert(test->name_size < sizeof(test->name));
|
||||
vsnprintf(test->name, sizeof(test->name), fmt, args);
|
||||
}
|
||||
|
||||
void DN_UTest_Begin(DN_UTest *test, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UTest_BeginV(test, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DN_UTest_End(DN_UTest *test)
|
||||
{
|
||||
assert(test->state != DN_UTestState_Nil && "Test was marked as ended but a test was never commenced using DN_UTest_Begin");
|
||||
size_t pad_size = DN_UTEST_RESULT_LPAD - (DN_UTEST_SPACING + test->name_size);
|
||||
if (pad_size < 0)
|
||||
pad_size = 0;
|
||||
|
||||
char pad_buffer[DN_UTEST_RESULT_LPAD] = {};
|
||||
memset(pad_buffer, DN_UTEST_RESULT_PAD_CHAR, pad_size);
|
||||
|
||||
printf("%*s%.*s%.*s", DN_UTEST_SPACING, "", (int)test->name_size, test->name, (int)pad_size, pad_buffer);
|
||||
if (test->state == DN_UTestState_TestFailed) {
|
||||
printf(DN_UTEST_BAD_COLOR " FAILED");
|
||||
} else {
|
||||
printf(DN_UTEST_GOOD_COLOR " OK");
|
||||
test->num_tests_ok_in_group++;
|
||||
}
|
||||
printf(DN_UTEST_COLOR_RESET "\n");
|
||||
test->state = DN_UTestState_Nil;
|
||||
}
|
||||
#endif // DN_UTEST_IMPLEMENTATION
|
@ -1,9 +1,8 @@
|
||||
---
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
---
|
||||
Language: Cpp
|
||||
|
||||
IndentWidth: 2
|
||||
TabWidth: 2
|
||||
|
||||
# Align parameters on the open bracket, e.g.:
|
||||
# someLongFunction(argument1,
|
||||
# argument2);
|
||||
@ -27,10 +26,10 @@ AlignArrayOfStructures: Left
|
||||
# int d = 3;
|
||||
# /* A comment. */
|
||||
# double e = 4;
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveBitFields: Consecutive
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveBitFields: Consecutive
|
||||
AlignConsecutiveDeclarations: Consecutive
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
|
||||
# Align escaped newlines as far left as possible.
|
||||
# #define A \
|
||||
@ -135,7 +134,7 @@ AlwaysBreakTemplateDeclarations: MultiLine
|
||||
# aaaaaaaaaaaaaaaaaaaa,
|
||||
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
|
||||
# }
|
||||
BinPackArguments: false
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false # As BinPackArguments but for function definition parameters
|
||||
|
||||
# Add space after the : only (space may be added before if needed for
|
||||
@ -154,9 +153,9 @@ BitFieldColonSpacing: After
|
||||
# ccccccccccccccccccccccccccccccccccccccccc;
|
||||
BreakBeforeBinaryOperators: None
|
||||
|
||||
# Always attach braces to surrounding context, but break before braces on
|
||||
# function, namespace and class definitions.
|
||||
BreakBeforeBraces: Linux
|
||||
# BS_Attach (in configuration: Attach) Always attach braces to surrounding context.
|
||||
# BS_Mozilla (in configuration: Mozilla) Like Attach, but break before braces on enum, function, and record definitions.
|
||||
BreakBeforeBraces: Mozilla
|
||||
|
||||
# true:
|
||||
# template<typename T>
|
||||
@ -197,7 +196,7 @@ BreakInheritanceList: AfterComma
|
||||
# "ryVeryVeryVeryVeryVery"
|
||||
# "VeryLongString";
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 100
|
||||
ColumnLimit: 0
|
||||
|
||||
# false:
|
||||
# namespace Foo {
|
||||
|
10
build.bat
10
build.bat
@ -13,15 +13,15 @@ pushd Build
|
||||
REM O2 Optimisation Level 2
|
||||
REM Oi Use CPU Intrinsics
|
||||
REM Z7 Combine multi-debug files to one debug file
|
||||
set common_flags=-D DN_UNIT_TESTS_WITH_KECCAK -D DN_USE_STD_PRINTF %script_dir%\dqn_unit_tests_main.cpp
|
||||
set common_flags=-D DN_UNIT_TESTS_WITH_KECCAK %script_dir%\Extra\dn_tests_main.cpp
|
||||
|
||||
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo
|
||||
|
||||
REM Optionally pass `-analyze` to `msvc_compile_flags` for more checks, but,
|
||||
REM it slows down compilation by around 5s on my old laptop.
|
||||
set msvc_compile_flags=%msvc_driver_flags% -analyze -fsanitize=address /Fe:dqn_unit_tests_msvc
|
||||
set clang_compile_flags=%msvc_driver_flags% -fsanitize=address -fsanitize=undefined /Fe:dqn_unit_tests_clang
|
||||
set zig_compile_flags=%common_flags% -fsanitize=address -fsanitize=undefined -o dqn_unit_tests_zig
|
||||
set msvc_compile_flags=%msvc_driver_flags% -analyze -fsanitize=address /Fe:dn_unit_tests_msvc
|
||||
set clang_compile_flags=%msvc_driver_flags% -fsanitize=address -fsanitize=undefined /Fe:dn_unit_tests_clang
|
||||
set zig_compile_flags=%common_flags% -fsanitize=address -fsanitize=undefined -o dn_unit_tests_zig
|
||||
|
||||
set msvc_link_flags=-link
|
||||
set clang_link_flags=%msvc_link_flags%
|
||||
@ -45,7 +45,7 @@ pushd Build
|
||||
)
|
||||
|
||||
REM zig ========================================================================================
|
||||
REM TODO(doyle):Can't build "Misc\dqn_unit_tests.cpp|1 col 1| error: unable to build C object: FileNotFound"
|
||||
REM TODO(doyle):Can't build "Misc\dn_unit_tests.cpp|1 col 1| error: unable to build C object: FileNotFound"
|
||||
REM set has_zig=1
|
||||
REM where /q zig || set has_zig=0
|
||||
REM if %has_zig% == 1 (
|
||||
|
8
build.sh
8
build.sh
@ -11,9 +11,9 @@ pushd Build
|
||||
-Werror \
|
||||
-fsanitize=address \
|
||||
-std=c++17 \
|
||||
-D DQN_UNIT_TESTS_WITH_MAIN \
|
||||
-D DQN_UNIT_TESTS_WITH_KECCAK \
|
||||
-x ${code_dir}/dqn.cpp \
|
||||
-D DN_UNIT_TESTS_WITH_MAIN \
|
||||
-D DN_UNIT_TESTS_WITH_KECCAK \
|
||||
-x ${code_dir}/dn.cpp \
|
||||
-g \
|
||||
-o dqn_unit_tests
|
||||
-o dn_unit_tests
|
||||
popd
|
||||
|
6
dn_base_inc.cpp
Normal file
6
dn_base_inc.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "Base/dn_base.cpp"
|
||||
#include "Base/dn_base_containers.cpp"
|
||||
#include "Base/dn_base_convert.cpp"
|
||||
#include "Base/dn_base_mem.cpp"
|
||||
#include "Base/dn_base_string.cpp"
|
||||
#include "Base/dn_base_log.cpp"
|
14
dn_base_inc.h
Normal file
14
dn_base_inc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#if !defined(DN_BASE_INC_H)
|
||||
#define DN_BASE_INC_H
|
||||
|
||||
#include "Base/dn_base_compiler.h"
|
||||
#include "Base/dn_base.h"
|
||||
#include "Base/dn_base_os.h"
|
||||
#include "Base/dn_base_assert.h"
|
||||
#include "Base/dn_base_mem.h"
|
||||
#include "Base/dn_base_log.h"
|
||||
#include "Base/dn_base_string.h"
|
||||
#include "Base/dn_base_containers.h"
|
||||
#include "Base/dn_base_convert.h"
|
||||
|
||||
#endif // !defined(DN_BASE_INC_H)
|
31
dn_clangd.h
Normal file
31
dn_clangd.h
Normal file
@ -0,0 +1,31 @@
|
||||
#if defined(_CLANDG)
|
||||
#include "dn_base_inc.h"
|
||||
#include "dn_core_inc.h"
|
||||
#include "dn_os_inc.h"
|
||||
|
||||
#include "Standalone/dn_cpp_file.h"
|
||||
#include "Standalone/dn_keccak.h"
|
||||
#include "Standalone/dn_utest.h"
|
||||
|
||||
#include "Extra/dn_cgen.h"
|
||||
#include "Extra/dn_csv.h"
|
||||
#include "Extra/dn_hash.h"
|
||||
#include "Extra/dn_helpers.h"
|
||||
#include "Extra/dn_json.h"
|
||||
#include "Extra/dn_math.h"
|
||||
#include "Extra/dn_type_info.h"
|
||||
|
||||
#include "Extra/dn_cgen.cpp"
|
||||
#include "Extra/dn_csv.cpp"
|
||||
#include "Extra/dn_hash.cpp"
|
||||
#include "Extra/dn_helpers.cpp"
|
||||
#include "Extra/dn_json.cpp"
|
||||
#include "Extra/dn_math.cpp"
|
||||
#include "Extra/dn_tests.cpp"
|
||||
#include "Extra/dn_type_info.cpp"
|
||||
|
||||
#include "dn_base_inc.cpp"
|
||||
#include "dn_core_inc.cpp"
|
||||
#include "dn_os_inc.cpp"
|
||||
#endif
|
||||
|
3
dn_core_inc.cpp
Normal file
3
dn_core_inc.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "Core/dn_core.cpp"
|
||||
#include "Core/dn_core_debug.cpp"
|
||||
#include "Core/dn_core_demo.cpp"
|
7
dn_core_inc.h
Normal file
7
dn_core_inc.h
Normal file
@ -0,0 +1,7 @@
|
||||
#if !defined(DN_CORE_INC_H)
|
||||
#define DN_CORE_INC_H
|
||||
|
||||
#include "Core/dn_core_debug.h"
|
||||
#include "Core/dn_core.h"
|
||||
|
||||
#endif // !defined(DN_CORE_INC_H)
|
14
dn_os_inc.cpp
Normal file
14
dn_os_inc.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "OS/dn_os_tls.cpp"
|
||||
#include "OS/dn_os.cpp"
|
||||
#include "OS/dn_os_allocator.cpp"
|
||||
#include "OS/dn_os_containers.cpp"
|
||||
#include "OS/dn_os_print.cpp"
|
||||
#include "OS/dn_os_string.cpp"
|
||||
|
||||
#if defined(DN_PLATFORM_POSIX)
|
||||
#include "OS/dn_os_posix.cpp"
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
#include "OS/dn_os_win32.cpp"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
13
dn_os_inc.h
Normal file
13
dn_os_inc.h
Normal file
@ -0,0 +1,13 @@
|
||||
#include "OS/dn_os_tls.h"
|
||||
#include "OS/dn_os.h"
|
||||
#include "OS/dn_os_allocator.h"
|
||||
#include "OS/dn_os_containers.h"
|
||||
#include "OS/dn_os_print.h"
|
||||
#include "OS/dn_os_string.h"
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
#include "OS/dn_os_windows.h"
|
||||
#include "OS/dn_os_win32.h"
|
||||
#elif defined(DN_PLATFORM_POSIX)
|
||||
#include "OS/dn_os_posix.h"
|
||||
#endif
|
74
dqn.cpp
74
dqn.cpp
@ -1,74 +0,0 @@
|
||||
#include "dqn.h"
|
||||
|
||||
#define DN_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
|
||||
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
|
||||
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
|
||||
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
|
||||
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
|
||||
// $$ | $$ |\$ /$$ |$$ | $$ |
|
||||
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
|
||||
// \______|\__| \__|\__| \________|
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if defined(DN_WITH_CGEN)
|
||||
#if !defined(DN_NO_METADESK)
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<function>': unreferenced function with internal linkage has been removed
|
||||
|
||||
DN_GCC_WARNING_PUSH
|
||||
DN_GCC_WARNING_DISABLE(-Wwrite-strings)
|
||||
DN_GCC_WARNING_DISABLE(-Wunused-but-set-variable)
|
||||
DN_GCC_WARNING_DISABLE(-Wsign-compare)
|
||||
DN_GCC_WARNING_DISABLE(-Wunused-function)
|
||||
DN_GCC_WARNING_DISABLE(-Wunused-result)
|
||||
|
||||
#include "External/metadesk/md.c"
|
||||
|
||||
DN_GCC_WARNING_POP
|
||||
DN_MSVC_WARNING_POP
|
||||
#endif
|
||||
#define DN_CPP_FILE_IMPLEMENTATION
|
||||
#include "Standalone/dqn_cpp_file.h"
|
||||
#include "dqn_cgen.cpp"
|
||||
#endif
|
||||
|
||||
#if defined(DN_WITH_JSON)
|
||||
#include "dqn_json.cpp"
|
||||
#endif
|
||||
|
||||
#include "dqn_base.cpp"
|
||||
#include "dqn_external.cpp"
|
||||
#include "dqn_allocator.cpp"
|
||||
#include "dqn_debug.cpp"
|
||||
#include "dqn_string.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_type_info.cpp"
|
||||
#include "dqn_os.cpp"
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_ARM64)
|
||||
#include "dqn_os_posix.cpp"
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
#include "dqn_os_win32.cpp"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
|
||||
#include "dqn_tls.cpp"
|
||||
#include "dqn_math.cpp"
|
||||
#include "dqn_hash.cpp"
|
||||
#include "dqn_helpers.cpp"
|
||||
|
||||
#if defined(DN_WITH_UNIT_TESTS)
|
||||
#include "dqn_unit_tests.cpp"
|
||||
#endif
|
||||
|
||||
#include "dqn_docs.cpp"
|
354
dqn.h
354
dqn.h
@ -1,354 +0,0 @@
|
||||
#pragma once
|
||||
#define DN_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |
|
||||
// $$ | $$ |$$ / $$ |$$$$\ $$ |
|
||||
// $$ | $$ |$$ | $$ |$$ $$\$$ |
|
||||
// $$ | $$ |$$ | $$ |$$ \$$$$ |
|
||||
// $$ | $$ |$$ $$\$$ |$$ |\$$$ |
|
||||
// $$$$$$$ |\$$$$$$ / $$ | \$$ |
|
||||
// \_______/ \___$$$\ \__| \__|
|
||||
// \___|
|
||||
//
|
||||
// dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dn
|
||||
// ASCII -- BigMoney-NW by Nathan Bloomfild
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This library is a single-header file-esque library with inspiration taken
|
||||
// from STB libraries for ease of integration and use. It defines a bunch of
|
||||
// primitives and standard library functions that are missing and or more
|
||||
// appropriate for development in modern day computing (e.g. allocator
|
||||
// first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
|
||||
// constrained by the language specification and operate closer to the OS).
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ _____|\__$$ __|\__$$ __|\_$$ _|$$$\ $$ |$$ __$$\
|
||||
// $$ / \__|$$ | $$ | $$ | $$ | $$$$\ $$ |$$ / \__|
|
||||
// $$ |$$$$\ $$$$$\ $$ | $$ | $$ | $$ $$\$$ |$$ |$$$$\
|
||||
// $$ |\_$$ |$$ __| $$ | $$ | $$ | $$ \$$$$ |$$ |\_$$ |
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ |
|
||||
// \$$$$$$ |$$$$$$$$\ $$ | $$ | $$$$$$\ $$ | \$$ |\$$$$$$ |
|
||||
// \______/ \________| \__| \__| \______|\__| \__| \______/
|
||||
//
|
||||
// Getting started -- Compiling with the library and library documentation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// -- Compiling --
|
||||
//
|
||||
// Compile dn.cpp or include it into one of your translation units.
|
||||
//
|
||||
// Additionally, this library supports including/excluding specific sections
|
||||
// of the library by using #define on the name of the section. These names are
|
||||
// documented in the section table of contents at the #define column, for
|
||||
// example:
|
||||
//
|
||||
// #define DN_ONLY_VARRAY
|
||||
// #define DN_ONLY_WIN
|
||||
//
|
||||
// Compiles the library with all optional APIs turned off except virtual arrays
|
||||
// and the Win32 helpers. Alternatively:
|
||||
//
|
||||
// #define DN_NO_VARRAY
|
||||
// #define DN_NO_WIN
|
||||
//
|
||||
// Compiles the library with all optional APIs turned on except the previously
|
||||
// mentioned APIs.
|
||||
//
|
||||
// -- Library documentation --
|
||||
//
|
||||
// The header files in this library are intentionally extremely minimal and
|
||||
// concise as to provide a dense reference of the APIs available without
|
||||
// drowning out the library interface with code comments like many other
|
||||
// documentation systems.
|
||||
//
|
||||
// Instead, documentation is laid out in dn_docs.cpp in alphabetical order
|
||||
// which provides self-contained examples in one contiguous top-down block of
|
||||
// source code with comments.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
|
||||
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
|
||||
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \______| \______/ \__| \__| \______/
|
||||
//
|
||||
// Options -- Compile time build customisation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// - Override these routines from the CRT by redefining them. By default we wrap
|
||||
// the CRT functions from <strings.h> and <math.h>, e.g:
|
||||
//
|
||||
// #define DN_MEMCPY(dest, src, count) memcpy(dest, src, value)
|
||||
// #define DN_MEMSET(dest, value, count) memset(dest, value, count)
|
||||
// #define DN_MEMCMP(lhs, rhs, count) memcpy(lhs, rhs, count)
|
||||
// #define DN_MEMMOVE(dest, src, count) memmove(dest, src, count)
|
||||
// #define DN_SQRTF(val) sqrtf(val)
|
||||
// #define DN_SINF(val) sinf(val)
|
||||
// #define DN_COSF(val) cosf(val)
|
||||
// #define DN_TANF(val) tanf(val)
|
||||
//
|
||||
// - Redefine 'DN_API' to change the prefix of all declared functions in the library
|
||||
//
|
||||
// #define DN_API
|
||||
//
|
||||
// - Define 'DN_STATIC_API' to apply 'static' to all function definitions and
|
||||
// disable external linkage to other translation units by redefining 'DN_API' to
|
||||
// 'static'.
|
||||
//
|
||||
// #define DN_STATIC_API
|
||||
//
|
||||
// - Turn all assertion macros to no-ops except for hard asserts (which are
|
||||
// always enabled and represent unrecoverable errors in the library).
|
||||
//
|
||||
// #define DN_NO_ASSERT
|
||||
//
|
||||
// - Augment DN_CHECK(expr) macro's behaviour. By default when the check fails a
|
||||
// debug break is emitted. If this macro is defined, the check will not trigger
|
||||
// a debug break.
|
||||
//
|
||||
// #define DN_NO_CHECK_BREAK
|
||||
//
|
||||
// - Define this macro to enable memory leak tracking on arena's that are
|
||||
// configured to track allocations.
|
||||
//
|
||||
// Allocations are stored in a global hash-table and their respective stack
|
||||
// traces for the allocation location. Memory leaks can be dumped at the end
|
||||
// of the program or some epoch by calling DN_Debug_DumpLeaks()
|
||||
//
|
||||
// #define DN_LEAK_TRACKING
|
||||
//
|
||||
// - Define this to revert to the family of printf functions from <stdio.h>
|
||||
// instead of using stb_sprintf in this library. stb_sprintf is 5-6x faster
|
||||
// than printf with a smaller binary footprint and has deterministic behaviour
|
||||
// across all supported platforms.
|
||||
//
|
||||
// #define DN_USE_STD_PRINTF
|
||||
//
|
||||
// However, if you are compiling with ASAN on MSVC, MSVC's implementation of
|
||||
// __declspec(no_sanitize_address) is unable to suppress warnings in some
|
||||
// individual functions in stb's implementation causing ASAN to trigger. This
|
||||
// library will error on compilation if it detects this is the case and is
|
||||
// being compiled with STB sprintf.
|
||||
//
|
||||
// - Define this to stop this library from defining a minimal subset of Win32
|
||||
// prototypes and definitions in this file. You should use this macro if you
|
||||
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
|
||||
// the redefined declarations in this library.
|
||||
//
|
||||
// #define DN_NO_WIN32_MIN_HEADER
|
||||
//
|
||||
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
|
||||
// Useful if another library uses and includes "stb_sprintf.h"
|
||||
//
|
||||
// #define DN_STB_SPRINTF_HEADER_ONLY
|
||||
//
|
||||
// - Override the default break into the active debugger function. By default
|
||||
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
|
||||
//
|
||||
// #define DN_DEBUG_BREAK
|
||||
//
|
||||
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
|
||||
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
|
||||
// by padding allocated before and after with poisoned memory which will raise
|
||||
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
|
||||
// not compiled with ASAN.
|
||||
//
|
||||
// #define DN_ASAN_POISON 1
|
||||
//
|
||||
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
|
||||
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
|
||||
// ensure that memory from arenas are correctly un/poisoned when pointers are
|
||||
// allocated and returned to the memory arena's. This is a no-op if we are not
|
||||
// compiled with ASAN or `DN_ASAN_POISON` is not set to `1`.
|
||||
//
|
||||
// #define DN_ASAN_VET_POISON 1
|
||||
//
|
||||
// - Define this macro to the size of the guard memory reserved before and after
|
||||
// allocations made that are poisoned to protect against out-of-bounds memory
|
||||
// accesses. By default the library sets the guard to 128 bytes.
|
||||
//
|
||||
// #define DN_ASAN_POISON_GUARD_SIZE 128
|
||||
//
|
||||
// - Enable 'DN_CGen' a parser that can emit run-time type information and
|
||||
// allow arbitrary querying of data definitions expressed in Excel-like tables
|
||||
// using text files encoded in Dion-System's Metadesk grammar.
|
||||
//
|
||||
// This option automatically includes 'dn_cpp_file.h' to assist with code
|
||||
// generation and Metadesk's 'md.h' and its implementation library.
|
||||
//
|
||||
// #define DN_WITH_CGEN
|
||||
//
|
||||
// Optionally define 'DN_NO_METADESK' to disable the inclusion of Metadesk
|
||||
// in the library. This might be useful if you are including the librarin in
|
||||
// your project yourself. This library must still be defined and visible
|
||||
// before this header.
|
||||
//
|
||||
// - Enable 'DN_JSON' a json parser. This option requires Sheredom's 'json.h'
|
||||
// to be included prior to this file.
|
||||
//
|
||||
// #define DN_WITH_JSON
|
||||
//
|
||||
// Optionally define 'DN_NO_SHEREDOM_JSON' to prevent Sheredom's 'json.h'
|
||||
// library from being included. This might be useful if you are including the
|
||||
// library in your project yourself. The library must still be defined and
|
||||
// visible before this header.
|
||||
//
|
||||
// - Enable compilation of unit tests with the library.
|
||||
//
|
||||
// #define DN_WITH_UNIT_TESTS
|
||||
//
|
||||
// - Increase the capacity of the job queue, default is 128.
|
||||
//
|
||||
// #define DN_JOB_QUEUE_SPMC_SIZE 128
|
||||
*/
|
||||
|
||||
#if defined(DN_ONLY_VARRAY) || \
|
||||
defined(DN_ONLY_SARRAY) || \
|
||||
defined(DN_ONLY_FARRAY) || \
|
||||
defined(DN_ONLY_DSMAP) || \
|
||||
defined(DN_ONLY_LIST) || \
|
||||
defined(DN_ONLY_FSTR8) || \
|
||||
defined(DN_ONLY_FS) || \
|
||||
defined(DN_ONLY_WINNET) || \
|
||||
defined(DN_ONLY_WIN) || \
|
||||
defined(DN_ONLY_SEMAPHORE) || \
|
||||
defined(DN_ONLY_THREAD) || \
|
||||
defined(DN_ONLY_V2) || \
|
||||
defined(DN_ONLY_V3) || \
|
||||
defined(DN_ONLY_V4) || \
|
||||
defined(DN_ONLY_M4) || \
|
||||
defined(DN_ONLY_RECT) || \
|
||||
defined(DN_ONLY_JSON_BUILDER) || \
|
||||
defined(DN_ONLY_BIN) || \
|
||||
defined(DN_ONLY_PROFILER)
|
||||
|
||||
#if !defined(DN_ONLY_VARRAY)
|
||||
#define DN_NO_VARRAY
|
||||
#endif
|
||||
#if !defined(DN_ONLY_FARRAY)
|
||||
#define DN_NO_FARRAY
|
||||
#endif
|
||||
#if !defined(DN_ONLY_SARRAY)
|
||||
#define DN_NO_SARRAY
|
||||
#endif
|
||||
#if !defined(DN_ONLY_DSMAP)
|
||||
#define DN_NO_DSMAP
|
||||
#endif
|
||||
#if !defined(DN_ONLY_LIST)
|
||||
#define DN_NO_LIST
|
||||
#endif
|
||||
#if !defined(DN_ONLY_FSTR8)
|
||||
#define DN_NO_FSTR8
|
||||
#endif
|
||||
#if !defined(DN_ONLY_FS)
|
||||
#define DN_NO_FS
|
||||
#endif
|
||||
#if !defined(DN_ONLY_WINNET)
|
||||
#define DN_NO_WINNET
|
||||
#endif
|
||||
#if !defined(DN_ONLY_WIN)
|
||||
#define DN_NO_WIN
|
||||
#endif
|
||||
#if !defined(DN_ONLY_SEMAPHORE)
|
||||
#define DN_NO_SEMAPHORE
|
||||
#endif
|
||||
#if !defined(DN_ONLY_THREAD)
|
||||
#define DN_NO_THREAD
|
||||
#endif
|
||||
#if !defined(DN_ONLY_V2)
|
||||
#define DN_NO_V2
|
||||
#endif
|
||||
#if !defined(DN_ONLY_V3)
|
||||
#define DN_NO_V3
|
||||
#endif
|
||||
#if !defined(DN_ONLY_V4)
|
||||
#define DN_NO_V4
|
||||
#endif
|
||||
#if !defined(DN_ONLY_M4)
|
||||
#define DN_NO_M4
|
||||
#endif
|
||||
#if !defined(DN_ONLY_RECT)
|
||||
#define DN_NO_RECT
|
||||
#endif
|
||||
#if !defined(DN_ONLY_JSON_BUILDER)
|
||||
#define DN_NO_JSON_BUILDER
|
||||
#endif
|
||||
#if !defined(DN_ONLY_BIN)
|
||||
#define DN_NO_BIN
|
||||
#endif
|
||||
#if !defined(DN_ONLY_PROFILER)
|
||||
#define DN_NO_PROFILER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "dqn_base.h"
|
||||
#if defined(DN_WITH_CGEN)
|
||||
#if !defined(DN_NO_METADESK)
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define DN_UNDO_CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
// NOTE: Metadesk does not have the header for 'size_t'
|
||||
#if defined(DN_COMPILER_GCC)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#define MD_DEFAULT_SPRINTF 0
|
||||
#define MD_IMPL_Vsnprintf DN_VSNPRINTF
|
||||
#include "External/metadesk/md.h"
|
||||
#if defined(DN_UNDO_CRT_SECURE_NO_WARNINGS)
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Metadesk includes Windows.h
|
||||
#define DN_NO_WIN32_MIN_HEADER
|
||||
#endif
|
||||
|
||||
#include "dqn_external.h"
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
#include "dqn_win32.h"
|
||||
#endif
|
||||
#include "dqn_allocator.h"
|
||||
#include "dqn_tls.h"
|
||||
#include "dqn_debug.h"
|
||||
#include "dqn_string.h"
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_ARM64)
|
||||
#include "dqn_os_posix.h"
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
#include "dqn_os_win32.h"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
#include "dqn_os.h"
|
||||
#include "dqn_containers.h"
|
||||
#include "dqn_math.h"
|
||||
#include "dqn_hash.h"
|
||||
#include "dqn_helpers.h"
|
||||
#include "dqn_type_info.h"
|
||||
|
||||
#if defined(DN_WITH_CGEN)
|
||||
#include "Standalone/dqn_cpp_file.h"
|
||||
#include "dqn_cgen.h"
|
||||
#endif
|
||||
|
||||
#if defined(DN_WITH_JSON)
|
||||
#if !defined(DN_NO_SHEREDOM_JSON)
|
||||
#include "External/json.h"
|
||||
#endif
|
||||
#include "dqn_json.h"
|
||||
#endif
|
@ -1,653 +0,0 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// $$ __$$\ $$ | $$ | $$ __$$\ $$ __$$\ $$ __$$\\__$$ __|$$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ / $$ |$$ / \__|$$ / $$ | $$ | $$ / $$ |$$ | $$ |
|
||||
// $$$$$$$$ |$$ | $$ | $$ | $$ |$$ | $$$$$$$$ | $$ | $$ | $$ |$$$$$$$ |
|
||||
// $$ __$$ |$$ | $$ | $$ | $$ |$$ | $$ __$$ | $$ | $$ | $$ |$$ __$$<
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$\ $$ | $$ | $$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$$$$$ |\$$$$$$ |$$ | $$ | $$ | $$$$$$ |$$ | $$ |
|
||||
// \__| \__|\________|\________|\______/ \______/ \__| \__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_allocator.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$AREN] DN_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_ArenaBlock *DN_Arena_BlockInit(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak)
|
||||
{
|
||||
DN_USize const page_size = g_dn_core->os_page_size;
|
||||
DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE;
|
||||
DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE;
|
||||
real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size);
|
||||
real_commit = DN_MIN(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve);
|
||||
|
||||
DN_ASSERTF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%I64u < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve);
|
||||
DN_ASSERTF(page_size, "Call DN_Library_Init() to initialise the known page size");
|
||||
|
||||
DN_OSMemCommit mem_commit = real_reserve == real_commit ? DN_OSMemCommit_Yes : DN_OSMemCommit_No;
|
||||
DN_ArenaBlock *result = DN_CAST(DN_ArenaBlock *)DN_OS_MemReserve(real_reserve, mem_commit, DN_OSMemPage_ReadWrite);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
if (mem_commit == DN_OSMemCommit_No && !DN_OS_MemCommit(result, real_commit, DN_OSMemPage_ReadWrite)) {
|
||||
DN_OS_MemRelease(result, real_reserve);
|
||||
return result;
|
||||
}
|
||||
|
||||
result->used = DN_ARENA_HEADER_SIZE;
|
||||
result->commit = real_commit;
|
||||
result->reserve = real_reserve;
|
||||
|
||||
if (track_alloc)
|
||||
DN_Debug_TrackAlloc(result, result->reserve, alloc_can_leak);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaBlock *DN_Arena_BlockInitFlags(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
|
||||
{
|
||||
bool track_alloc = (flags & DN_ArenaFlags_NoAllocTrack) == 0;
|
||||
bool alloc_can_leak = flags & DN_ArenaFlags_AllocCanLeak;
|
||||
DN_ArenaBlock *result = DN_Arena_BlockInit(reserve, commit, track_alloc, alloc_can_leak);
|
||||
if (result && ((flags & DN_ArenaFlags_NoPoison) == 0))
|
||||
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *)result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_Arena_UpdateStatsOnNewBlock_(DN_Arena *arena, DN_ArenaBlock const *block)
|
||||
{
|
||||
DN_ASSERT(arena);
|
||||
if (block) {
|
||||
arena->stats.info.used += block->used;
|
||||
arena->stats.info.commit += block->commit;
|
||||
arena->stats.info.reserve += block->reserve;
|
||||
arena->stats.info.blocks += 1;
|
||||
|
||||
arena->stats.hwm.used = DN_MAX(arena->stats.hwm.used, arena->stats.info.used);
|
||||
arena->stats.hwm.commit = DN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
arena->stats.hwm.reserve = DN_MAX(arena->stats.hwm.reserve, arena->stats.info.reserve);
|
||||
arena->stats.hwm.blocks = DN_MAX(arena->stats.hwm.blocks, arena->stats.info.blocks);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitBuffer(void *buffer, DN_USize size, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_ASSERT(buffer);
|
||||
DN_ASSERTF(DN_ARENA_HEADER_SIZE < size, "Buffer (%zu bytes) too small, need atleast %zu bytes to store arena metadata", size, DN_ARENA_HEADER_SIZE);
|
||||
DN_ASSERTF(DN_IsPowerOfTwo(size), "Buffer (%zu bytes) must be a power-of-two", size);
|
||||
|
||||
// NOTE: Init block
|
||||
DN_ArenaBlock *block = DN_CAST(DN_ArenaBlock *) buffer;
|
||||
block->commit = size;
|
||||
block->reserve = size;
|
||||
block->used = DN_ARENA_HEADER_SIZE;
|
||||
if (block && ((flags & DN_ArenaFlags_NoPoison) == 0)) {
|
||||
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *)block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE);
|
||||
}
|
||||
|
||||
DN_Arena result = {};
|
||||
result.flags = flags | DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_UserBuffer;
|
||||
result.curr = block;
|
||||
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitSize(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_Arena result = {};
|
||||
result.flags = flags;
|
||||
result.curr = DN_Arena_BlockInitFlags(reserve, commit, flags);
|
||||
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_Arena_BlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
|
||||
{
|
||||
DN_USize release_size = block->reserve;
|
||||
if (DN_Bit_IsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
|
||||
DN_Debug_TrackDealloc(block);
|
||||
DN_ASAN_UnpoisonMemoryRegion(block, block->commit);
|
||||
DN_OS_MemRelease(block, release_size);
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Deinit(DN_Arena *arena)
|
||||
{
|
||||
for (DN_ArenaBlock *block = arena ? arena->curr : nullptr; block; ) {
|
||||
DN_ArenaBlock *block_to_free = block;
|
||||
block = block->prev;
|
||||
DN_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
if (arena)
|
||||
*arena = {};
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_CommitTo(DN_Arena *arena, DN_U64 pos)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
if (pos <= curr->commit)
|
||||
return true;
|
||||
|
||||
DN_U64 real_pos = pos;
|
||||
if (!DN_CHECK(pos <= curr->reserve))
|
||||
real_pos = curr->reserve;
|
||||
|
||||
DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, g_dn_core->os_page_size);
|
||||
DN_USize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
|
||||
if (!DN_OS_MemCommit(commit_ptr, commit_size, DN_OSMemPage_ReadWrite))
|
||||
return false;
|
||||
|
||||
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
|
||||
if (poison)
|
||||
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
|
||||
curr->commit = end_commit;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_Commit(DN_Arena *arena, DN_U64 size)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
DN_U64 pos = arena->curr->commit + size;
|
||||
bool result = DN_Arena_CommitTo(arena, pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_Grow(DN_Arena *arena, DN_U64 reserve, DN_U64 commit)
|
||||
{
|
||||
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
|
||||
return false;
|
||||
|
||||
DN_ArenaBlock *new_block = DN_Arena_BlockInitFlags(reserve, commit, arena->flags);
|
||||
if (new_block) {
|
||||
new_block->prev = arena->curr;
|
||||
arena->curr = new_block;
|
||||
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
|
||||
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
}
|
||||
bool result = new_block;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_Alloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
if (!arena->curr) {
|
||||
arena->curr = DN_Arena_BlockInitFlags(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, arena->flags);
|
||||
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
}
|
||||
|
||||
if (!arena->curr)
|
||||
return nullptr;
|
||||
|
||||
try_alloc_again:
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
|
||||
uint8_t real_align = poison ? DN_MAX(align, DN_ASAN_POISON_ALIGNMENT) : align;
|
||||
DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0);
|
||||
DN_U64 end_pos = offset_pos + size;
|
||||
DN_U64 alloc_size = end_pos - curr->used;
|
||||
|
||||
if (end_pos > curr->reserve) {
|
||||
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
|
||||
return nullptr;
|
||||
DN_USize new_reserve = DN_MAX(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE);
|
||||
DN_USize new_commit = DN_MAX(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE);
|
||||
if (!DN_Arena_Grow(arena, new_reserve, new_commit))
|
||||
return nullptr;
|
||||
goto try_alloc_again;
|
||||
}
|
||||
|
||||
DN_USize prev_arena_commit = curr->commit;
|
||||
if (end_pos > curr->commit) {
|
||||
DN_ASSERT((arena->flags & DN_ArenaFlags_UserBuffer) == 0);
|
||||
DN_USize end_commit = DN_AlignUpPowerOfTwo(end_pos, g_dn_core->os_page_size);
|
||||
DN_USize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
|
||||
if (!DN_OS_MemCommit(commit_ptr, commit_size, DN_OSMemPage_ReadWrite))
|
||||
return nullptr;
|
||||
if (poison)
|
||||
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
curr->commit = end_commit;
|
||||
arena->stats.info.commit += commit_size;
|
||||
arena->stats.hwm.commit = DN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
}
|
||||
|
||||
void *result = DN_CAST(char *) curr + offset_pos;
|
||||
curr->used += alloc_size;
|
||||
arena->stats.info.used += alloc_size;
|
||||
arena->stats.hwm.used = DN_MAX(arena->stats.hwm.used, arena->stats.info.used);
|
||||
DN_ASAN_UnpoisonMemoryRegion(result, size);
|
||||
|
||||
if (zero_mem == DN_ZeroMem_Yes) {
|
||||
DN_USize reused_bytes = DN_MIN(prev_arena_commit - offset_pos, size);
|
||||
DN_MEMSET(result, 0, reused_bytes);
|
||||
}
|
||||
|
||||
DN_ASSERT(arena->stats.hwm.used >= arena->stats.info.used);
|
||||
DN_ASSERT(arena->stats.hwm.commit >= arena->stats.info.commit);
|
||||
DN_ASSERT(arena->stats.hwm.reserve >= arena->stats.info.reserve);
|
||||
DN_ASSERT(arena->stats.hwm.blocks >= arena->stats.info.blocks);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_AllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_ArenaFlags prev_flags = arena->flags;
|
||||
arena->flags |= (DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoPoison);
|
||||
void *memory = DN_Arena_Alloc(arena, size, align, zero_mem);
|
||||
arena->flags = prev_flags;
|
||||
return memory;
|
||||
}
|
||||
|
||||
DN_API void *DN_Arena_Copy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align)
|
||||
{
|
||||
if (!arena || !data || size == 0)
|
||||
return nullptr;
|
||||
void *result = DN_Arena_Alloc(arena, size, align, DN_ZeroMem_No);
|
||||
if (result)
|
||||
DN_MEMCPY(result, data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_PopTo(DN_Arena *arena, DN_U64 init_used)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return;
|
||||
DN_U64 used = DN_MAX(DN_ARENA_HEADER_SIZE, init_used);
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
while (curr->reserve_sum >= used) {
|
||||
DN_ArenaBlock *block_to_free = curr;
|
||||
arena->stats.info.used -= block_to_free->used;
|
||||
arena->stats.info.commit -= block_to_free->commit;
|
||||
arena->stats.info.reserve -= block_to_free->reserve;
|
||||
arena->stats.info.blocks -= 1;
|
||||
if (arena->flags & DN_ArenaFlags_UserBuffer)
|
||||
break;
|
||||
curr = curr->prev;
|
||||
DN_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
|
||||
arena->stats.info.used -= curr->used;
|
||||
arena->curr = curr;
|
||||
curr->used = used - curr->reserve_sum;
|
||||
char *poison_ptr = (char *)curr + DN_AlignUpPowerOfTwo(curr->used, DN_ASAN_POISON_ALIGNMENT);
|
||||
DN_USize poison_size = ((char *)curr + curr->commit) - poison_ptr;
|
||||
DN_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
|
||||
arena->stats.info.used += curr->used;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Pop(DN_Arena *arena, DN_U64 amount)
|
||||
{
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
DN_USize used_sum = curr->reserve_sum + curr->used;
|
||||
if (!DN_CHECK(amount <= used_sum))
|
||||
amount = used_sum;
|
||||
DN_USize pop_to = used_sum - amount;
|
||||
DN_Arena_PopTo(arena, pop_to);
|
||||
}
|
||||
|
||||
DN_API DN_U64 DN_Arena_Pos(DN_Arena const *arena)
|
||||
{
|
||||
DN_U64 result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Arena_Clear(DN_Arena *arena)
|
||||
{
|
||||
DN_Arena_PopTo(arena, 0);
|
||||
}
|
||||
|
||||
DN_API bool DN_Arena_OwnsPtr(DN_Arena const *arena, void *ptr)
|
||||
{
|
||||
bool result = false;
|
||||
uintptr_t uint_ptr = DN_CAST(uintptr_t)ptr;
|
||||
for (DN_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
|
||||
uintptr_t begin = DN_CAST(uintptr_t) block + DN_ARENA_HEADER_SIZE;
|
||||
uintptr_t end = begin + block->reserve;
|
||||
result = uint_ptr >= begin && uint_ptr <= end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumStatsArray(DN_ArenaStats const *array, DN_USize size)
|
||||
{
|
||||
DN_ArenaStats result = {};
|
||||
DN_FOR_UINDEX(index, size) {
|
||||
DN_ArenaStats stats = array[index];
|
||||
result.info.used += stats.info.used;
|
||||
result.info.commit += stats.info.commit;
|
||||
result.info.reserve += stats.info.reserve;
|
||||
result.info.blocks += stats.info.blocks;
|
||||
|
||||
result.hwm.used = DN_MAX(result.hwm.used, result.info.used);
|
||||
result.hwm.commit = DN_MAX(result.hwm.commit, result.info.commit);
|
||||
result.hwm.reserve = DN_MAX(result.hwm.reserve, result.info.reserve);
|
||||
result.hwm.blocks = DN_MAX(result.hwm.blocks, result.info.blocks);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumStats(DN_ArenaStats lhs, DN_ArenaStats rhs)
|
||||
{
|
||||
DN_ArenaStats array[] = {lhs, rhs};
|
||||
DN_ArenaStats result = DN_Arena_SumStatsArray(array, DN_ARRAY_UCOUNT(array));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats(DN_Arena const *array, DN_USize size)
|
||||
{
|
||||
DN_ArenaStats result = {};
|
||||
for (DN_USize index = 0; index < size; index++) {
|
||||
DN_Arena const *arena = array + index;
|
||||
result = DN_Arena_SumStats(result, arena->stats);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin(DN_Arena *arena)
|
||||
{
|
||||
DN_ArenaTempMem result = {};
|
||||
if (arena) {
|
||||
DN_ArenaBlock *curr = arena->curr;
|
||||
result = {arena, curr ? curr->reserve_sum + curr->used : 0};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
DN_API void DN_Arena_TempMemEnd(DN_ArenaTempMem mem)
|
||||
{
|
||||
DN_Arena_PopTo(mem.arena, mem.used_sum);
|
||||
};
|
||||
|
||||
DN_ArenaTempMemScope::DN_ArenaTempMemScope(DN_Arena *arena)
|
||||
{
|
||||
mem = DN_Arena_TempMemBegin(arena);
|
||||
}
|
||||
|
||||
DN_ArenaTempMemScope::~DN_ArenaTempMemScope()
|
||||
{
|
||||
DN_Arena_TempMemEnd(mem);
|
||||
}
|
||||
|
||||
// NOTE: [$POOL] DN_Pool //////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Pool DN_Pool_Init(DN_Arena *arena, uint8_t align)
|
||||
{
|
||||
DN_Pool result = {};
|
||||
if (arena) {
|
||||
result.arena = arena;
|
||||
result.align = align ? align : DN_POOL_DEFAULT_ALIGN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_Pool_IsValid(DN_Pool const *pool)
|
||||
{
|
||||
bool result = pool && pool->arena && pool->align;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_Pool_Alloc(DN_Pool *pool, DN_USize size)
|
||||
{
|
||||
void *result = nullptr;
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
DN_USize const required_size = sizeof(DN_PoolSlot) + pool->align + size;
|
||||
DN_USize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. DN_PoolSlotSize_32B
|
||||
DN_USize slot_index = 0;
|
||||
if (required_size > 32) {
|
||||
// NOTE: Round up if not PoT as the low bits are set.
|
||||
DN_USize dist_to_next_msb = DN_CountLeadingZerosU64(required_size) + 1;
|
||||
dist_to_next_msb -= DN_CAST(DN_USize)(!DN_IsPowerOfTwo(required_size));
|
||||
|
||||
DN_USize const register_size = sizeof(DN_USize) * 8;
|
||||
DN_ASSERT(register_size >= dist_to_next_msb + size_to_slot_offset);
|
||||
slot_index = register_size - dist_to_next_msb - size_to_slot_offset;
|
||||
}
|
||||
|
||||
if (!DN_CHECKF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size"))
|
||||
return result;
|
||||
|
||||
DN_USize slot_size_in_bytes = 1ULL << (slot_index + size_to_slot_offset);
|
||||
DN_ASSERT(required_size <= (slot_size_in_bytes << 0));
|
||||
DN_ASSERT(required_size >= (slot_size_in_bytes >> 1));
|
||||
|
||||
DN_PoolSlot *slot = nullptr;
|
||||
if (pool->slots[slot_index]) {
|
||||
slot = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot->next;
|
||||
DN_MEMSET(slot->data, 0, size);
|
||||
DN_ASSERT(DN_IsPowerOfTwoAligned(slot->data, pool->align));
|
||||
} else {
|
||||
void *bytes = DN_Arena_Alloc(pool->arena, slot_size_in_bytes, alignof(DN_PoolSlot), DN_ZeroMem_Yes);
|
||||
slot = DN_CAST(DN_PoolSlot *) bytes;
|
||||
|
||||
// NOTE: The raw pointer is round up to the next 'pool->align'-ed
|
||||
// address ensuring at least 1 byte of padding between the raw pointer
|
||||
// and the pointer given to the user and that the user pointer is
|
||||
// aligned to the pool's alignment.
|
||||
//
|
||||
// This allows us to smuggle 1 byte behind the user pointer that has
|
||||
// the offset to the original pointer.
|
||||
slot->data = DN_CAST(void *)DN_AlignDownPowerOfTwo(DN_CAST(uintptr_t)slot + sizeof(DN_PoolSlot) + pool->align, pool->align);
|
||||
|
||||
uintptr_t offset_to_original_ptr = DN_CAST(uintptr_t)slot->data - DN_CAST(uintptr_t)bytes;
|
||||
DN_ASSERT(slot->data > bytes);
|
||||
DN_ASSERT(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
|
||||
|
||||
// NOTE: Store the offset to the original pointer behind the user's
|
||||
// pointer.
|
||||
char *offset_to_original_storage = DN_CAST(char *)slot->data - 1;
|
||||
DN_MEMCPY(offset_to_original_storage, &offset_to_original_ptr, 1);
|
||||
}
|
||||
|
||||
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
|
||||
// pointer gets returned which free list to return the pointer to.
|
||||
result = slot->data;
|
||||
slot->next = DN_CAST(DN_PoolSlot *)slot_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8FV(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
DN_USize size_required = DN_CStr8_FVSize(fmt, args);
|
||||
result.data = DN_CAST(char *) DN_Pool_Alloc(pool, size_required + 1);
|
||||
if (result.data) {
|
||||
result.size = size_required;
|
||||
DN_VSNPRINTF(result.data, DN_CAST(int)(result.size + 1), fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8F(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Pool_AllocStr8FV(pool, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8Copy(DN_Pool *pool, DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Pool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
if (!DN_Str8_HasData(string))
|
||||
return result;
|
||||
|
||||
char *data = DN_CAST(char *)DN_Pool_Alloc(pool, string.size + 1);
|
||||
if (!data)
|
||||
return result;
|
||||
|
||||
DN_MEMCPY(data, string.data, string.size);
|
||||
data[string.size] = 0;
|
||||
result = DN_Str8_Init(data, string.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Pool_Dealloc(DN_Pool *pool, void *ptr)
|
||||
{
|
||||
if (!DN_Pool_IsValid(pool) || !ptr)
|
||||
return;
|
||||
|
||||
DN_ASSERT(DN_Arena_OwnsPtr(pool->arena, ptr));
|
||||
|
||||
char const *one_byte_behind_ptr = DN_CAST(char *) ptr - 1;
|
||||
DN_USize offset_to_original_ptr = 0;
|
||||
DN_MEMCPY(&offset_to_original_ptr, one_byte_behind_ptr, 1);
|
||||
DN_ASSERT(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
|
||||
|
||||
char *original_ptr = DN_CAST(char *) ptr - offset_to_original_ptr;
|
||||
DN_PoolSlot *slot = DN_CAST(DN_PoolSlot *) original_ptr;
|
||||
DN_PoolSlotSize slot_index = DN_CAST(DN_PoolSlotSize)(DN_CAST(uintptr_t) slot->next);
|
||||
DN_ASSERT(slot_index < DN_PoolSlotSize_Count);
|
||||
|
||||
slot->next = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot;
|
||||
}
|
||||
|
||||
DN_API void *DN_Pool_Copy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align)
|
||||
{
|
||||
if (!pool || !data || size == 0)
|
||||
return nullptr;
|
||||
|
||||
// TODO: Hmm should align be part of the alloc interface in general? I'm not going to worry
|
||||
// about this until we crash because of misalignment.
|
||||
DN_ASSERT(pool->align >= align);
|
||||
|
||||
void *result = DN_Pool_Alloc(pool, size);
|
||||
if (result)
|
||||
DN_MEMCPY(result, data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$ACAT] DN_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_ArenaCatalog_Init(DN_ArenaCatalog *catalog, DN_Pool *pool)
|
||||
{
|
||||
catalog->pool = pool;
|
||||
catalog->sentinel.next = &catalog->sentinel;
|
||||
catalog->sentinel.prev = &catalog->sentinel;
|
||||
}
|
||||
|
||||
DN_API DN_ArenaCatalogItem *DN_ArenaCatalog_Find(DN_ArenaCatalog *catalog, DN_Str8 label)
|
||||
{
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
DN_ArenaCatalogItem *result = &catalog->sentinel;
|
||||
for (DN_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
|
||||
if (item->label == label) {
|
||||
result = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_ArenaCatalog_AddInternal_(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_Str8 label, bool arena_pool_allocated)
|
||||
{
|
||||
// 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.
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
|
||||
// NOTE: Create item in the catalog
|
||||
DN_ArenaCatalogItem *result = DN_Pool_New(catalog->pool, DN_ArenaCatalogItem);
|
||||
if (result) {
|
||||
result->arena = arena;
|
||||
result->label = label;
|
||||
result->arena_pool_allocated = arena_pool_allocated;
|
||||
|
||||
// NOTE: Add to the catalog (linked list)
|
||||
DN_ArenaCatalogItem *sentinel = &catalog->sentinel;
|
||||
result->next = sentinel;
|
||||
result->prev = sentinel->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
DN_Atomic_AddU32(&catalog->arena_count, 1);
|
||||
}
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
}
|
||||
|
||||
DN_API void DN_ArenaCatalog_AddF(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
va_end(args);
|
||||
DN_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
|
||||
}
|
||||
|
||||
DN_API void DN_ArenaCatalog_AddFV(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
DN_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_ArenaCatalog_AllocFV(DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
DN_Arena *result = DN_Pool_New(catalog->pool, DN_Arena);
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
|
||||
*result = DN_Arena_InitSize(reserve, commit, arena_flags);
|
||||
DN_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_ArenaCatalog_AllocF(DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
DN_Str8 label = DN_Pool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
DN_Arena *result = DN_Pool_New(catalog->pool, DN_Arena);
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
va_end(args);
|
||||
|
||||
*result = DN_Arena_InitSize(reserve, commit, arena_flags);
|
||||
DN_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArenaCatalog_Erase(DN_ArenaCatalog *catalog, DN_Arena *arena, DN_ArenaCatalogFreeArena free_arena)
|
||||
{
|
||||
bool result = false;
|
||||
DN_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
for (DN_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
|
||||
if (item->arena == arena) {
|
||||
item->next->prev = item->prev;
|
||||
item->prev->next = item->next;
|
||||
if (item->arena_pool_allocated) {
|
||||
if (free_arena == DN_ArenaCatalogFreeArena_Yes)
|
||||
DN_Arena_Deinit(item->arena);
|
||||
DN_Pool_Dealloc(catalog->pool, item->arena);
|
||||
}
|
||||
DN_Pool_Dealloc(catalog->pool, item->label.data);
|
||||
DN_Pool_Dealloc(catalog->pool, item);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DN_TicketMutex_End(&catalog->ticket_mutex);
|
||||
return result;
|
||||
}
|
220
dqn_allocator.h
220
dqn_allocator.h
@ -1,220 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// $$ __$$\ $$ | $$ | $$ __$$\ $$ __$$\ $$ __$$\\__$$ __|$$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ / $$ |$$ / \__|$$ / $$ | $$ | $$ / $$ |$$ | $$ |
|
||||
// $$$$$$$$ |$$ | $$ | $$ | $$ |$$ | $$$$$$$$ | $$ | $$ | $$ |$$$$$$$ |
|
||||
// $$ __$$ |$$ | $$ | $$ | $$ |$$ | $$ __$$ | $$ | $$ | $$ |$$ __$$<
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$\ $$ | $$ | $$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$$$$$ |\$$$$$$ |$$ | $$ | $$ | $$$$$$ |$$ | $$ |
|
||||
// \__| \__|\________|\________|\______/ \______/ \__| \__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_allocator.h -- Custom memory allocators
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$AREN] DN_Arena -- Growing bump allocator
|
||||
// [$CHUN] DN_Pool -- Allocates reusable, free-able memory in PoT chunks
|
||||
// [$POOL] DN_Pool -- TODO
|
||||
// [$ACAT] DN_ArenaCatalog -- Collate, create & manage arenas in a catalog
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$AREN] DN_Arena /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_ARENA_RESERVE_SIZE)
|
||||
#define DN_ARENA_RESERVE_SIZE DN_MEGABYTES(64)
|
||||
#endif
|
||||
#if !defined(DN_ARENA_COMMIT_SIZE)
|
||||
#define DN_ARENA_COMMIT_SIZE DN_KILOBYTES(64)
|
||||
#endif
|
||||
|
||||
struct DN_ArenaBlock
|
||||
{
|
||||
DN_ArenaBlock *prev;
|
||||
DN_U64 used;
|
||||
DN_U64 commit;
|
||||
DN_U64 reserve;
|
||||
DN_U64 reserve_sum;
|
||||
};
|
||||
|
||||
typedef uint32_t DN_ArenaFlags;
|
||||
enum DN_ArenaFlags_
|
||||
{
|
||||
DN_ArenaFlags_Nil = 0,
|
||||
DN_ArenaFlags_NoGrow = 1 << 0,
|
||||
DN_ArenaFlags_NoPoison = 1 << 1,
|
||||
DN_ArenaFlags_NoAllocTrack = 1 << 2,
|
||||
DN_ArenaFlags_AllocCanLeak = 1 << 3,
|
||||
DN_ArenaFlags_UserBuffer = 1 << 4,
|
||||
};
|
||||
|
||||
struct DN_ArenaInfo
|
||||
{
|
||||
DN_U64 used;
|
||||
DN_U64 commit;
|
||||
DN_U64 reserve;
|
||||
DN_U64 blocks;
|
||||
};
|
||||
|
||||
struct DN_ArenaStats
|
||||
{
|
||||
DN_ArenaInfo info;
|
||||
DN_ArenaInfo hwm;
|
||||
};
|
||||
|
||||
struct DN_Arena
|
||||
{
|
||||
DN_ArenaBlock *curr;
|
||||
DN_ArenaStats stats;
|
||||
DN_ArenaFlags flags;
|
||||
DN_Str8 label;
|
||||
DN_Arena *prev, *next;
|
||||
};
|
||||
|
||||
struct DN_ArenaTempMem
|
||||
{
|
||||
DN_Arena *arena;
|
||||
DN_U64 used_sum;
|
||||
};
|
||||
|
||||
struct DN_ArenaTempMemScope
|
||||
{
|
||||
DN_ArenaTempMemScope(DN_Arena *arena);
|
||||
~DN_ArenaTempMemScope();
|
||||
DN_ArenaTempMem mem;
|
||||
};
|
||||
|
||||
DN_USize const DN_ARENA_HEADER_SIZE = DN_AlignUpPowerOfTwo(sizeof(DN_Arena), 64);
|
||||
|
||||
// NOTE: [$CHUN] DN_Pool /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_POOL_DEFAULT_ALIGN)
|
||||
#define DN_POOL_DEFAULT_ALIGN 16
|
||||
#endif
|
||||
|
||||
struct DN_PoolSlot
|
||||
{
|
||||
void *data;
|
||||
DN_PoolSlot *next;
|
||||
};
|
||||
|
||||
enum DN_PoolSlotSize
|
||||
{
|
||||
DN_PoolSlotSize_32B,
|
||||
DN_PoolSlotSize_64B,
|
||||
DN_PoolSlotSize_128B,
|
||||
DN_PoolSlotSize_256B,
|
||||
DN_PoolSlotSize_512B,
|
||||
DN_PoolSlotSize_1KiB,
|
||||
DN_PoolSlotSize_2KiB,
|
||||
DN_PoolSlotSize_4KiB,
|
||||
DN_PoolSlotSize_8KiB,
|
||||
DN_PoolSlotSize_16KiB,
|
||||
DN_PoolSlotSize_32KiB,
|
||||
DN_PoolSlotSize_64KiB,
|
||||
DN_PoolSlotSize_128KiB,
|
||||
DN_PoolSlotSize_256KiB,
|
||||
DN_PoolSlotSize_512KiB,
|
||||
DN_PoolSlotSize_1MiB,
|
||||
DN_PoolSlotSize_2MiB,
|
||||
DN_PoolSlotSize_4MiB,
|
||||
DN_PoolSlotSize_8MiB,
|
||||
DN_PoolSlotSize_16MiB,
|
||||
DN_PoolSlotSize_32MiB,
|
||||
DN_PoolSlotSize_64MiB,
|
||||
DN_PoolSlotSize_128MiB,
|
||||
DN_PoolSlotSize_256MiB,
|
||||
DN_PoolSlotSize_512MiB,
|
||||
DN_PoolSlotSize_1GiB,
|
||||
DN_PoolSlotSize_2GiB,
|
||||
DN_PoolSlotSize_4GiB,
|
||||
DN_PoolSlotSize_8GiB,
|
||||
DN_PoolSlotSize_16GiB,
|
||||
DN_PoolSlotSize_32GiB,
|
||||
DN_PoolSlotSize_Count,
|
||||
};
|
||||
|
||||
struct DN_Pool
|
||||
{
|
||||
DN_Arena *arena;
|
||||
DN_PoolSlot *slots[DN_PoolSlotSize_Count];
|
||||
uint8_t align;
|
||||
};
|
||||
|
||||
// NOTE: [$ACAT] DN_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
struct DN_ArenaCatalogItem
|
||||
{
|
||||
DN_Arena *arena;
|
||||
DN_Str8 label;
|
||||
bool arena_pool_allocated;
|
||||
DN_ArenaCatalogItem *next;
|
||||
DN_ArenaCatalogItem *prev;
|
||||
};
|
||||
|
||||
struct DN_ArenaCatalog
|
||||
{
|
||||
DN_TicketMutex ticket_mutex; // Mutex for adding to the linked list of arenas
|
||||
DN_Pool *pool;
|
||||
DN_ArenaCatalogItem sentinel;
|
||||
uint16_t arena_count;
|
||||
};
|
||||
|
||||
enum DN_ArenaCatalogFreeArena
|
||||
{
|
||||
DN_ArenaCatalogFreeArena_No,
|
||||
DN_ArenaCatalogFreeArena_Yes,
|
||||
};
|
||||
|
||||
// NOTE: [$AREN] DN_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Arena DN_Arena_InitBuffer (void *buffer, DN_USize size, DN_ArenaFlags flags);
|
||||
DN_API DN_Arena DN_Arena_InitSize (DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags);
|
||||
DN_API void DN_Arena_Deinit (DN_Arena *arena);
|
||||
DN_API bool DN_Arena_Commit (DN_Arena *arena, DN_U64 size);
|
||||
DN_API bool DN_Arena_CommitTo (DN_Arena *arena, DN_U64 pos);
|
||||
DN_API bool DN_Arena_Grow (DN_Arena *arena, DN_U64 reserve, DN_U64 commit);
|
||||
DN_API void * DN_Arena_Alloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
|
||||
DN_API void * DN_Arena_AllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
|
||||
DN_API void * DN_Arena_Copy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align);
|
||||
DN_API void DN_Arena_PopTo (DN_Arena *arena, DN_U64 init_used);
|
||||
DN_API void DN_Arena_Pop (DN_Arena *arena, DN_U64 amount);
|
||||
DN_API DN_U64 DN_Arena_Pos (DN_Arena const *arena);
|
||||
DN_API void DN_Arena_Clear (DN_Arena *arena);
|
||||
DN_API bool DN_Arena_OwnsPtr (DN_Arena const *arena, void *ptr);
|
||||
DN_API DN_ArenaStats DN_Arena_SumStatsArray (DN_ArenaStats const *array, DN_USize size);
|
||||
DN_API DN_ArenaStats DN_Arena_SumStats (DN_ArenaStats lhs, DN_ArenaStats rhs);
|
||||
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats (DN_Arena const *array, DN_USize size);
|
||||
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin (DN_Arena *arena);
|
||||
DN_API void DN_Arena_TempMemEnd (DN_ArenaTempMem mem);
|
||||
#define DN_Arena_New_Frame(T, zero_mem) (T *)DN_Arena_Alloc(DN_TLS_Get()->frame_arena, sizeof(T), alignof(T), zero_mem)
|
||||
#define DN_Arena_New(arena, T, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
|
||||
#define DN_Arena_NewArray(arena, T, count, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T) * (count), alignof(T), zero_mem)
|
||||
#define DN_Arena_NewCopy(arena, T, src) (T *)DN_Arena_Copy (arena, (src), sizeof(T), alignof(T))
|
||||
#define DN_Arena_NewArrayCopy(arena, T, src, count) (T *)DN_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
// NOTE: [$CHUN] DN_Pool /////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Pool DN_Pool_Init (DN_Arena *arena, uint8_t align);
|
||||
DN_API bool DN_Pool_IsValid (DN_Pool const *pool);
|
||||
DN_API void * DN_Pool_Alloc (DN_Pool *pool, DN_USize size);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8FV (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8F (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Pool_AllocStr8Copy (DN_Pool *pool, DN_Str8 string);
|
||||
DN_API void DN_Pool_Dealloc (DN_Pool *pool, void *ptr);
|
||||
DN_API void * DN_Pool_Copy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align);
|
||||
|
||||
#define DN_Pool_New(pool, T) (T *)DN_Pool_Alloc(pool, sizeof(T))
|
||||
#define DN_Pool_NewArray(pool, T, count) (T *)DN_Pool_Alloc(pool, count * sizeof(T))
|
||||
#define DN_Pool_NewCopy(arena, T, src) (T *)DN_Pool_Copy (arena, (src), sizeof(T), alignof(T))
|
||||
#define DN_Pool_NewArrayCopy(arena, T, src, count) (T *)DN_Pool_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
// NOTE: [$ACAT] DN_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_ArenaCatalog_Init (DN_ArenaCatalog *catalog, DN_Pool *pool);
|
||||
DN_API DN_ArenaCatalogItem *DN_ArenaCatalog_Find (DN_ArenaCatalog *catalog, DN_Str8 label);
|
||||
DN_API void DN_ArenaCatalog_AddF (DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_ArenaCatalog_AddFV (DN_ArenaCatalog *catalog, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Arena * DN_ArenaCatalog_AllocFV (DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Arena * DN_ArenaCatalog_AllocF (DN_ArenaCatalog *catalog, DN_USize reserve, DN_USize commit, uint8_t arena_flags, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_ArenaCatalog_Erase (DN_ArenaCatalog *catalog, DN_Arena *arena, DN_ArenaCatalogFreeArena free_arena);
|
288
dqn_avx512f.cpp
288
dqn_avx512f.cpp
@ -1,288 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// /$$$$$$ /$$ /$$ /$$ /$$ /$$$$$$$ /$$ /$$$$$$ /$$$$$$$$
|
||||
// /$$__ $$| $$ | $$| $$ / $$ | $$____/ /$$$$ /$$__ $$| $$_____/
|
||||
// | $$ \ $$| $$ | $$| $$/ $$/ | $$ |_ $$ |__/ \ $$| $$
|
||||
// | $$$$$$$$| $$ / $$/ \ $$$$/ /$$$$$$| $$$$$$$ | $$ /$$$$$$/| $$$$$
|
||||
// | $$__ $$ \ $$ $$/ >$$ $$|______/|_____ $$ | $$ /$$____/ | $$__/
|
||||
// | $$ | $$ \ $$$/ /$$/\ $$ /$$ \ $$ | $$ | $$ | $$
|
||||
// | $$ | $$ \ $/ | $$ \ $$ | $$$$$$//$$$$$$| $$$$$$$$| $$
|
||||
// |__/ |__/ \_/ |__/ |__/ \______/|______/|________/|__/
|
||||
//
|
||||
// dqn_avx512f.h
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
DN_API DN_Str8FindResult DN_Str8_FindStr8AVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
|
||||
DN_Str8FindResult result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size)
|
||||
return result;
|
||||
|
||||
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
|
||||
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
|
||||
|
||||
DN_USize const search_size = string.size - find.size;
|
||||
DN_USize simd_iterations = search_size / sizeof(__m512i);
|
||||
char const *ptr = string.data;
|
||||
|
||||
while (simd_iterations--) {
|
||||
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
|
||||
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
|
||||
|
||||
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
|
||||
// where matches are found.
|
||||
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
|
||||
|
||||
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
|
||||
// operation using the ternarylogic intrinsic.
|
||||
//
|
||||
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
|
||||
// B = find_last_ch_block
|
||||
// C = find_last_ch
|
||||
//
|
||||
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
|
||||
//
|
||||
// / A / B / C / B ^ C / A | (B ^ C) /
|
||||
// | 0 | 0 | 0 | 0 | 0 |
|
||||
// | 0 | 0 | 1 | 1 | 1 |
|
||||
// | 0 | 1 | 0 | 1 | 1 |
|
||||
// | 0 | 1 | 1 | 0 | 0 |
|
||||
// | 1 | 0 | 0 | 0 | 1 |
|
||||
// | 1 | 0 | 1 | 1 | 1 |
|
||||
// | 1 | 1 | 0 | 1 | 1 |
|
||||
// | 1 | 1 | 1 | 0 | 1 |
|
||||
|
||||
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
|
||||
|
||||
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
|
||||
// out which 32 bit elements in the vector had zero bytes. This uses a
|
||||
// bit twiddling trick
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
|
||||
__mmask16 zero_byte_mask = {};
|
||||
{
|
||||
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
|
||||
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
|
||||
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
|
||||
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
|
||||
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
|
||||
}
|
||||
|
||||
while (zero_byte_mask) {
|
||||
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
|
||||
char const *base_ptr = ptr + (4 * lsb_zero_pos);
|
||||
|
||||
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data;
|
||||
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 1;
|
||||
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 2;
|
||||
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 3;
|
||||
}
|
||||
|
||||
if (result.found) {
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, result.index);
|
||||
result.match = DN_Str8_Init(string.data + result.index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - result.index);
|
||||
result.after_match_to_end_of_buffer = DN_Str8_Advance(result.match_to_end_of_buffer, find.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
|
||||
}
|
||||
|
||||
ptr += sizeof(__m512i);
|
||||
}
|
||||
|
||||
for (DN_USize index = ptr - string.data; index < string.size; index++) {
|
||||
DN_Str8 string_slice = DN_Str8_Slice(string, index, find.size);
|
||||
if (DN_Str8_Eq(string_slice, find)) {
|
||||
result.found = true;
|
||||
result.index = index;
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, index);
|
||||
result.match = DN_Str8_Init(string.data + index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - index);
|
||||
result.after_match_to_end_of_buffer = DN_Str8_Advance(result.match_to_end_of_buffer, find.size);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8FindResult DN_Str8_FindLastStr8AVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
// NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html
|
||||
DN_Str8FindResult result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size)
|
||||
return result;
|
||||
|
||||
__m512i const find_first_ch = _mm512_set1_epi8(find.data[0]);
|
||||
__m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]);
|
||||
|
||||
DN_USize const search_size = string.size - find.size;
|
||||
DN_USize simd_iterations = search_size / sizeof(__m512i);
|
||||
char const *ptr = string.data + search_size + 1;
|
||||
|
||||
while (simd_iterations--) {
|
||||
ptr -= sizeof(__m512i);
|
||||
__m512i find_first_ch_block = _mm512_loadu_si512(ptr);
|
||||
__m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1);
|
||||
|
||||
// NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit
|
||||
// where matches are found.
|
||||
__m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch);
|
||||
|
||||
// NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one
|
||||
// operation using the ternarylogic intrinsic.
|
||||
//
|
||||
// A = first_ch_matches (find_first_ch_block ^ find_first_ch)
|
||||
// B = find_last_ch_block
|
||||
// C = find_last_ch
|
||||
//
|
||||
// ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6
|
||||
//
|
||||
// / A / B / C / B ^ C / A | (B ^ C) /
|
||||
// | 0 | 0 | 0 | 0 | 0 |
|
||||
// | 0 | 0 | 1 | 1 | 1 |
|
||||
// | 0 | 1 | 0 | 1 | 1 |
|
||||
// | 0 | 1 | 1 | 0 | 0 |
|
||||
// | 1 | 0 | 0 | 0 | 1 |
|
||||
// | 1 | 0 | 1 | 1 | 1 |
|
||||
// | 1 | 1 | 0 | 1 | 1 |
|
||||
// | 1 | 1 | 1 | 0 | 1 |
|
||||
|
||||
__m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6);
|
||||
|
||||
// NOTE: Matches were XOR-ed and are hence indicated as zero so we mask
|
||||
// out which 32 bit elements in the vector had zero bytes. This uses a
|
||||
// bit twiddling trick
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
|
||||
__mmask16 zero_byte_mask = {};
|
||||
{
|
||||
const __m512i v01 = _mm512_set1_epi32(0x01010101u);
|
||||
const __m512i v80 = _mm512_set1_epi32(0x80808080u);
|
||||
const __m512i v1 = _mm512_sub_epi32(ch_matches, v01);
|
||||
const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20);
|
||||
zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1);
|
||||
}
|
||||
|
||||
while (zero_byte_mask) {
|
||||
uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask);
|
||||
char const *base_ptr = ptr + (4 * lsb_zero_pos);
|
||||
|
||||
if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data;
|
||||
} else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 1;
|
||||
} else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 2;
|
||||
} else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) {
|
||||
result.found = true;
|
||||
result.index = base_ptr - string.data + 3;
|
||||
}
|
||||
|
||||
if (result.found) {
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, result.index);
|
||||
result.match = DN_Str8_Init(string.data + result.index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - result.index);
|
||||
return result;
|
||||
}
|
||||
|
||||
zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask);
|
||||
}
|
||||
}
|
||||
|
||||
for (DN_USize index = ptr - string.data - 1; index < string.size; index--) {
|
||||
DN_Str8 string_slice = DN_Str8_Slice(string, index, find.size);
|
||||
if (DN_Str8_Eq(string_slice, find)) {
|
||||
result.found = true;
|
||||
result.index = index;
|
||||
result.start_to_before_match = DN_Str8_Init(string.data, index);
|
||||
result.match = DN_Str8_Init(string.data + index, find.size);
|
||||
result.match_to_end_of_buffer = DN_Str8_Init(result.match.data, string.size - index);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
DN_Str8BinarySplitResult result = {};
|
||||
DN_Str8FindResult find_result = DN_Str8_FindStr8AVX512F(string, find);
|
||||
if (find_result.found) {
|
||||
result.lhs.data = string.data;
|
||||
result.lhs.size = find_result.index;
|
||||
result.rhs = DN_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
|
||||
} else {
|
||||
result.lhs = string;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitLastAVX512F(DN_Str8 string, DN_Str8 find)
|
||||
{
|
||||
DN_Str8BinarySplitResult result = {};
|
||||
DN_Str8FindResult find_result = DN_Str8_FindLastStr8AVX512F(string, find);
|
||||
if (find_result.found) {
|
||||
result.lhs.data = string.data;
|
||||
result.lhs.size = find_result.index;
|
||||
result.rhs = DN_Str8_Advance(find_result.match_to_end_of_buffer, find.size);
|
||||
} else {
|
||||
result.lhs = string;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_Str8_SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_USize result = 0; // The number of splits in the actual string.
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(delimiter) || delimiter.size <= 0)
|
||||
return result;
|
||||
|
||||
DN_Str8BinarySplitResult split = {};
|
||||
DN_Str8 first = string;
|
||||
do {
|
||||
split = DN_Str8_BinarySplitAVX512F(first, delimiter);
|
||||
if (split.lhs.size || mode == DN_Str8SplitIncludeEmptyStrings_Yes) {
|
||||
if (splits && result < splits_count)
|
||||
splits[result] = split.lhs;
|
||||
result++;
|
||||
}
|
||||
first = split.rhs;
|
||||
} while (first.size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = {};
|
||||
DN_USize splits_required = DN_Str8_SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
|
||||
result.data = DN_Arena_NewArray(arena, DN_Str8, splits_required, DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
result.size = DN_Str8_SplitAVX512F(string, delimiter, result.data, splits_required, mode);
|
||||
DN_ASSERT(splits_required == result.size);
|
||||
}
|
||||
return result;
|
||||
}
|
837
dqn_base.cpp
837
dqn_base.cpp
@ -1,837 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
// $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$$$$$$\ | \____$$\ $$ _____|$$ __$$\
|
||||
// $$ __$$\ $$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ | $$ |$$ __$$ | \____$$\ $$ ____|
|
||||
// $$$$$$$ |\$$$$$$$ |$$$$$$$ |\$$$$$$$\
|
||||
// \_______/ \_______|\_______/ \_______|
|
||||
//
|
||||
// dn_base.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#if defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
||||
|
||||
DN_API DN_CPUIDResult DN_CPU_ID(DN_CPUIDArgs args)
|
||||
{
|
||||
DN_CPUIDResult result = {};
|
||||
__cpuidex(result.values, args.eax, args.ecx);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_USize DN_CPU_HasFeatureArray(DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size)
|
||||
{
|
||||
DN_USize result = 0;
|
||||
DN_USize const BITS = sizeof(report->features[0]) * 8;
|
||||
DN_FOR_UINDEX(feature_index, features_size) {
|
||||
DN_CPUFeatureQuery *query = features + feature_index;
|
||||
DN_USize chunk_index = query->feature / BITS;
|
||||
DN_USize chunk_bit = query->feature % BITS;
|
||||
uint64_t chunk = report->features[chunk_index];
|
||||
query->available = chunk & (1ULL << chunk_bit);
|
||||
result += DN_CAST(int)query->available;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_CPU_HasFeature(DN_CPUReport const *report, DN_CPUFeature feature)
|
||||
{
|
||||
DN_CPUFeatureQuery query = {};
|
||||
query.feature = feature;
|
||||
bool result = DN_CPU_HasFeatureArray(report, &query, 1) == 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_CPU_HasAllFeatures(DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size)
|
||||
{
|
||||
bool result = true;
|
||||
for (DN_USize index = 0; result && index < features_size; index++)
|
||||
result &= DN_CPU_HasFeature(report, features[index]);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_CPU_SetFeature(DN_CPUReport *report, DN_CPUFeature feature)
|
||||
{
|
||||
DN_ASSERT(feature < DN_CPUFeature_Count);
|
||||
DN_USize const BITS = sizeof(report->features[0]) * 8;
|
||||
DN_USize chunk_index = feature / BITS;
|
||||
DN_USize chunk_bit = feature % BITS;
|
||||
report->features[chunk_index] |= (1ULL << chunk_bit);
|
||||
}
|
||||
|
||||
DN_API DN_CPUReport DN_CPU_Report()
|
||||
{
|
||||
DN_CPUReport result = {};
|
||||
DN_CPUIDResult fn_0000_[500] = {};
|
||||
DN_CPUIDResult fn_8000_[500] = {};
|
||||
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
|
||||
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
|
||||
|
||||
// NOTE: Query standard/extended numbers ///////////////////////////////////////////////////////
|
||||
{
|
||||
DN_CPUIDArgs args = {};
|
||||
|
||||
// NOTE: Query standard function (e.g. eax = 0x0) for function count + cpu vendor
|
||||
args = {};
|
||||
fn_0000_[0] = DN_CPU_ID(args);
|
||||
|
||||
// NOTE: Query extended function (e.g. eax = 0x8000'0000) for function count + cpu vendor
|
||||
args = {};
|
||||
args.eax = DN_CAST(int) EXTENDED_FUNC_BASE_EAX;
|
||||
fn_8000_[0] = DN_CPU_ID(args);
|
||||
}
|
||||
|
||||
// NOTE: Extract function count ////////////////////////////////////////////////////////////////
|
||||
int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax;
|
||||
int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax;
|
||||
|
||||
// NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
|
||||
{
|
||||
DN_ASSERTF((STANDARD_FUNC_MAX_EAX + 1) <= DN_ARRAY_ICOUNT(fn_0000_),
|
||||
"Max standard count is %d", STANDARD_FUNC_MAX_EAX + 1);
|
||||
DN_ASSERTF((DN_CAST(DN_ISize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DN_ARRAY_ICOUNT(fn_8000_),
|
||||
"Max extended count is %zu", DN_CAST(DN_ISize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1);
|
||||
|
||||
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
|
||||
DN_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_0000_[eax] = DN_CPU_ID(args);
|
||||
}
|
||||
|
||||
for (int eax = EXTENDED_FUNC_BASE_EAX + 1, index = 1; eax <= EXTENDED_FUNC_MAX_EAX; eax++, index++) {
|
||||
DN_CPUIDArgs args = {};
|
||||
args.eax = eax;
|
||||
fn_8000_[index] = DN_CPU_ID(args);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Query CPU vendor //////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DN_MEMCPY(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU brand ///////////////////////////////////////////////////////////////////////
|
||||
if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) {
|
||||
DN_MEMCPY(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 8, &fn_8000_[0x0002].reg.ecx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 12, &fn_8000_[0x0002].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_MEMCPY(result.brand + 16, &fn_8000_[0x0003].reg.eax, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 20, &fn_8000_[0x0003].reg.ebx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 24, &fn_8000_[0x0003].reg.ecx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 28, &fn_8000_[0x0003].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_MEMCPY(result.brand + 32, &fn_8000_[0x0004].reg.eax, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 36, &fn_8000_[0x0004].reg.ebx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 40, &fn_8000_[0x0004].reg.ecx, REGISTER_SIZE);
|
||||
DN_MEMCPY(result.brand + 44, &fn_8000_[0x0004].reg.edx, REGISTER_SIZE);
|
||||
|
||||
DN_ASSERT(result.brand[sizeof(result.brand) - 1] == 0);
|
||||
}
|
||||
|
||||
// NOTE: Query CPU features //////////////////////////////////////////////////////////////////
|
||||
for (DN_USize ext_index = 0; ext_index < DN_CPUFeature_Count; ext_index++) {
|
||||
bool available = false;
|
||||
|
||||
// NOTE: Mask bits taken from various manuals
|
||||
// - AMD64 Architecture Programmer's Manual, Volumes 1-5
|
||||
// - https://en.wikipedia.org/wiki/CPUID#Calling_CPUID
|
||||
switch (DN_CAST(DN_CPUFeature)ext_index) {
|
||||
case DN_CPUFeature_3DNow: available = (fn_8000_[0x0001].reg.edx & (1 << 31)); break;
|
||||
case DN_CPUFeature_3DNowExt: available = (fn_8000_[0x0001].reg.edx & (1 << 30)); break;
|
||||
case DN_CPUFeature_ABM: available = (fn_8000_[0x0001].reg.ecx & (1 << 5)); break;
|
||||
case DN_CPUFeature_AES: available = (fn_0000_[0x0001].reg.ecx & (1 << 25)); break;
|
||||
case DN_CPUFeature_AVX: available = (fn_0000_[0x0001].reg.ecx & (1 << 28)); break;
|
||||
case DN_CPUFeature_AVX2: available = (fn_0000_[0x0007].reg.ebx & (1 << 0)); break;
|
||||
case DN_CPUFeature_AVX512F: available = (fn_0000_[0x0007].reg.ebx & (1 << 16)); break;
|
||||
case DN_CPUFeature_AVX512DQ: available = (fn_0000_[0x0007].reg.ebx & (1 << 17)); break;
|
||||
case DN_CPUFeature_AVX512IFMA: available = (fn_0000_[0x0007].reg.ebx & (1 << 21)); break;
|
||||
case DN_CPUFeature_AVX512PF: available = (fn_0000_[0x0007].reg.ebx & (1 << 26)); break;
|
||||
case DN_CPUFeature_AVX512ER: available = (fn_0000_[0x0007].reg.ebx & (1 << 27)); break;
|
||||
case DN_CPUFeature_AVX512CD: available = (fn_0000_[0x0007].reg.ebx & (1 << 28)); break;
|
||||
case DN_CPUFeature_AVX512BW: available = (fn_0000_[0x0007].reg.ebx & (1 << 30)); break;
|
||||
case DN_CPUFeature_AVX512VL: available = (fn_0000_[0x0007].reg.ebx & (1 << 31)); break;
|
||||
case DN_CPUFeature_AVX512VBMI: available = (fn_0000_[0x0007].reg.ecx & (1 << 1)); break;
|
||||
case DN_CPUFeature_AVX512VBMI2: available = (fn_0000_[0x0007].reg.ecx & (1 << 6)); break;
|
||||
case DN_CPUFeature_AVX512VNNI: available = (fn_0000_[0x0007].reg.ecx & (1 << 11)); break;
|
||||
case DN_CPUFeature_AVX512BITALG: available = (fn_0000_[0x0007].reg.ecx & (1 << 12)); break;
|
||||
case DN_CPUFeature_AVX512VPOPCNTDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 14)); break;
|
||||
case DN_CPUFeature_AVX5124VNNIW: available = (fn_0000_[0x0007].reg.edx & (1 << 2)); break;
|
||||
case DN_CPUFeature_AVX5124FMAPS: available = (fn_0000_[0x0007].reg.edx & (1 << 3)); break;
|
||||
case DN_CPUFeature_AVX512VP2INTERSECT: available = (fn_0000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_AVX512FP16: available = (fn_0000_[0x0007].reg.edx & (1 << 23)); break;
|
||||
case DN_CPUFeature_CLZERO: available = (fn_8000_[0x0008].reg.ebx & (1 << 0)); break;
|
||||
case DN_CPUFeature_CMPXCHG8B: available = (fn_0000_[0x0001].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_CMPXCHG16B: available = (fn_0000_[0x0001].reg.ecx & (1 << 13)); break;
|
||||
case DN_CPUFeature_F16C: available = (fn_0000_[0x0001].reg.ecx & (1 << 29)); break;
|
||||
case DN_CPUFeature_FMA: available = (fn_0000_[0x0001].reg.ecx & (1 << 12)); break;
|
||||
case DN_CPUFeature_FMA4: available = (fn_8000_[0x0001].reg.ecx & (1 << 16)); break;
|
||||
case DN_CPUFeature_FP128: available = (fn_8000_[0x001A].reg.eax & (1 << 0)); break;
|
||||
case DN_CPUFeature_FP256: available = (fn_8000_[0x001A].reg.eax & (1 << 2)); break;
|
||||
case DN_CPUFeature_FPU: available = (fn_0000_[0x0001].reg.edx & (1 << 0)); break;
|
||||
case DN_CPUFeature_MMX: available = (fn_0000_[0x0001].reg.edx & (1 << 23)); break;
|
||||
case DN_CPUFeature_MONITOR: available = (fn_0000_[0x0001].reg.ecx & (1 << 3)); break;
|
||||
case DN_CPUFeature_MOVBE: available = (fn_0000_[0x0001].reg.ecx & (1 << 22)); break;
|
||||
case DN_CPUFeature_MOVU: available = (fn_8000_[0x001A].reg.eax & (1 << 1)); break;
|
||||
case DN_CPUFeature_MmxExt: available = (fn_8000_[0x0001].reg.edx & (1 << 22)); break;
|
||||
case DN_CPUFeature_PCLMULQDQ: available = (fn_0000_[0x0001].reg.ecx & (1 << 1)); break;
|
||||
case DN_CPUFeature_POPCNT: available = (fn_0000_[0x0001].reg.ecx & (1 << 23)); break;
|
||||
case DN_CPUFeature_RDRAND: available = (fn_0000_[0x0001].reg.ecx & (1 << 30)); break;
|
||||
case DN_CPUFeature_RDSEED: available = (fn_0000_[0x0007].reg.ebx & (1 << 18)); break;
|
||||
case DN_CPUFeature_RDTSCP: available = (fn_8000_[0x0001].reg.edx & (1 << 27)); break;
|
||||
case DN_CPUFeature_SHA: available = (fn_0000_[0x0007].reg.ebx & (1 << 29)); break;
|
||||
case DN_CPUFeature_SSE: available = (fn_0000_[0x0001].reg.edx & (1 << 25)); break;
|
||||
case DN_CPUFeature_SSE2: available = (fn_0000_[0x0001].reg.edx & (1 << 26)); break;
|
||||
case DN_CPUFeature_SSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 0)); break;
|
||||
case DN_CPUFeature_SSE41: available = (fn_0000_[0x0001].reg.ecx & (1 << 19)); break;
|
||||
case DN_CPUFeature_SSE42: available = (fn_0000_[0x0001].reg.ecx & (1 << 20)); break;
|
||||
case DN_CPUFeature_SSE4A: available = (fn_8000_[0x0001].reg.ecx & (1 << 6)); break;
|
||||
case DN_CPUFeature_SSSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 9)); break;
|
||||
case DN_CPUFeature_TSC: available = (fn_0000_[0x0001].reg.edx & (1 << 4)); break;
|
||||
case DN_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break;
|
||||
case DN_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break;
|
||||
case DN_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break;
|
||||
case DN_CPUFeature_Count: DN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
|
||||
if (available)
|
||||
DN_CPU_SetFeature(&result, DN_CAST(DN_CPUFeature)ext_index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
|
||||
// NOTE: [$TMUT] DN_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex)
|
||||
{
|
||||
unsigned int ticket = DN_Atomic_AddU32(&mutex->ticket, 1);
|
||||
DN_TicketMutex_BeginTicket(mutex, ticket);
|
||||
}
|
||||
|
||||
DN_API void DN_TicketMutex_End(DN_TicketMutex *mutex)
|
||||
{
|
||||
DN_Atomic_AddU32(&mutex->serving, 1);
|
||||
}
|
||||
|
||||
DN_API DN_UInt DN_TicketMutex_MakeTicket(DN_TicketMutex *mutex)
|
||||
{
|
||||
DN_UInt result = DN_Atomic_AddU32(&mutex->ticket, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_TicketMutex_BeginTicket(DN_TicketMutex const *mutex, DN_UInt ticket)
|
||||
{
|
||||
DN_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();
|
||||
}
|
||||
}
|
||||
|
||||
DN_API bool DN_TicketMutex_CanLock(DN_TicketMutex const *mutex, DN_UInt ticket)
|
||||
{
|
||||
bool result = (ticket == mutex->serving);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#if !defined(DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED)
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: [$PRIN] DN_Print /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_PrintStyle DN_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, DN_PrintBold bold)
|
||||
{
|
||||
DN_PrintStyle result = {};
|
||||
result.bold = bold;
|
||||
result.colour = true;
|
||||
result.r = r;
|
||||
result.g = g;
|
||||
result.b = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_PrintStyle DN_Print_StyleColourU32(uint32_t rgb, DN_PrintBold bold)
|
||||
{
|
||||
uint8_t r = (rgb >> 24) & 0xFF;
|
||||
uint8_t g = (rgb >> 16) & 0xFF;
|
||||
uint8_t b = (rgb >> 8) & 0xFF;
|
||||
DN_PrintStyle result = DN_Print_StyleColour(r, g, b, bold);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_PrintStyle DN_Print_StyleBold()
|
||||
{
|
||||
DN_PrintStyle result = {};
|
||||
result.bold = DN_PrintBold_Yes;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_Print_Std(DN_PrintStd std_handle, DN_Str8 string)
|
||||
{
|
||||
DN_ASSERT(std_handle == DN_PrintStd_Out || std_handle == DN_PrintStd_Err);
|
||||
|
||||
#if defined(DN_OS_WIN32)
|
||||
// NOTE: Get the output handles from kernel ////////////////////////////////////////////////////
|
||||
DN_THREAD_LOCAL void *std_out_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL void *std_err_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL bool std_out_print_to_console = false;
|
||||
DN_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 == DN_PrintStd_Err) {
|
||||
print_handle = std_err_print_handle;
|
||||
print_to_console = std_err_print_to_console;
|
||||
}
|
||||
|
||||
// NOTE: Write the string //////////////////////////////////////////////////////////////////////
|
||||
DN_ASSERT(string.size < DN_CAST(unsigned long)-1);
|
||||
unsigned long bytes_written = 0; (void)bytes_written;
|
||||
if (print_to_console) {
|
||||
WriteConsoleA(print_handle, string.data, DN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||
} else {
|
||||
WriteFile(print_handle, string.data, DN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||
}
|
||||
#else
|
||||
fprintf(std_handle == DN_PrintStd_Out ? stdout : stderr, "%.*s", DN_STR_FMT(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_Str8 string)
|
||||
{
|
||||
if (string.data && string.size) {
|
||||
if (style.colour)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCColourFgStr8(style.r, style.g, style.b));
|
||||
if (style.bold == DN_PrintBold_Yes)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCBoldStr8);
|
||||
DN_Print_Std(std_handle, string);
|
||||
if (style.colour || style.bold == DN_PrintBold_Yes)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCResetStr8);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdF(DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Print_StdFV(std_handle, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdFStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#if !defined(DN_USE_STD_PRINTF)
|
||||
DN_FILE_SCOPE char *DN_Print_VSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
DN_Str8 string = {};
|
||||
string.data = DN_CAST(char *)buf;
|
||||
string.size = len;
|
||||
|
||||
DN_PrintStd std_handle = DN_CAST(DN_PrintStd)DN_CAST(uintptr_t)user;
|
||||
DN_Print_Std(std_handle, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
DN_API void DN_Print_StdFV(DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
#if defined(DN_USE_STD_PRINTF)
|
||||
vfprintf(std_handle == DN_PrintStd_Out ? stdout : stderr, fmt, args);
|
||||
#else
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)(DN_Print_VSPrintfChunker_, DN_CAST(void *)DN_CAST(uintptr_t)std_handle, buffer, fmt, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdFVStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (fmt) {
|
||||
if (style.colour)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCColourFgStr8(style.r, style.g, style.b));
|
||||
if (style.bold == DN_PrintBold_Yes)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCBoldStr8);
|
||||
DN_Print_StdFV(std_handle, fmt, args);
|
||||
if (style.colour || style.bold == DN_PrintBold_Yes)
|
||||
DN_Print_Std(std_handle, DN_Print_ESCResetStr8);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLn(DN_PrintStd std_handle, DN_Str8 string)
|
||||
{
|
||||
DN_Print_Std(std_handle, string);
|
||||
DN_Print_Std(std_handle, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLnF(DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Print_StdLnFV(std_handle, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLnFV(DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Print_StdFV(std_handle, fmt, args);
|
||||
DN_Print_Std(std_handle, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLnStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_Str8 string)
|
||||
{
|
||||
DN_Print_StdStyle(std_handle, style, string);
|
||||
DN_Print_Std(std_handle, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLnFStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Print_StdLnFVStyle(std_handle, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_Print_StdLnFVStyle(DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||
DN_Print_Std(std_handle, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Print_ESCColourStr8(DN_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
DN_THREAD_LOCAL char buffer[32];
|
||||
buffer[0] = 0;
|
||||
DN_Str8 result = {};
|
||||
result.size = DN_SNPRINTF(buffer,
|
||||
DN_ARRAY_UCOUNT(buffer),
|
||||
"\x1b[%d;2;%u;%u;%um",
|
||||
colour == DN_PrintESCColour_Fg ? 38 : 48,
|
||||
r, g, b);
|
||||
result.data = buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Print_ESCColourU32Str8(DN_PrintESCColour colour, uint32_t value)
|
||||
{
|
||||
uint8_t r = DN_CAST(uint8_t)(value >> 24);
|
||||
uint8_t g = DN_CAST(uint8_t)(value >> 16);
|
||||
uint8_t b = DN_CAST(uint8_t)(value >> 8);
|
||||
DN_Str8 result = DN_Print_ESCColourStr8(colour, r, g, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$LLOG] DN_Log ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_Log_MakeStr8(DN_Arena *arena,
|
||||
bool colour,
|
||||
DN_Str8 type,
|
||||
int log_type,
|
||||
DN_CallSite call_site,
|
||||
DN_FMT_ATTRIB char const *fmt,
|
||||
va_list args)
|
||||
{
|
||||
DN_LOCAL_PERSIST DN_USize max_type_length = 0;
|
||||
max_type_length = DN_MAX(max_type_length, type.size);
|
||||
int type_padding = DN_CAST(int)(max_type_length - type.size);
|
||||
|
||||
DN_Str8 colour_esc = {};
|
||||
DN_Str8 bold_esc = {};
|
||||
DN_Str8 reset_esc = {};
|
||||
if (colour) {
|
||||
bold_esc = DN_Print_ESCBoldStr8;
|
||||
reset_esc = DN_Print_ESCResetStr8;
|
||||
switch (log_type) {
|
||||
case DN_LogType_Debug: break;
|
||||
case DN_LogType_Info: colour_esc = DN_Print_ESCColourFgU32Str8(DN_LogTypeColourU32_Info); break;
|
||||
case DN_LogType_Warning: colour_esc = DN_Print_ESCColourFgU32Str8(DN_LogTypeColourU32_Warning); break;
|
||||
case DN_LogType_Error: colour_esc = DN_Print_ESCColourFgU32Str8(DN_LogTypeColourU32_Error); break;
|
||||
}
|
||||
}
|
||||
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(call_site.file);
|
||||
DN_OSDateTimeStr8 const time = DN_OS_DateLocalTimeStr8Now();
|
||||
DN_Str8 header = DN_Str8_InitF(arena,
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
"%.*s" // bold
|
||||
"%.*s" // type
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05I32u " // line number
|
||||
,
|
||||
DN_CAST(int)time.date_size - 2, time.date + 2, // date
|
||||
DN_CAST(int)time.hms_size, time.hms, // hms
|
||||
DN_STR_FMT(colour_esc), // colour
|
||||
DN_STR_FMT(bold_esc), // bold
|
||||
DN_STR_FMT(type), // type
|
||||
DN_CAST(int)type_padding, "", // type padding
|
||||
DN_STR_FMT(reset_esc), // reset
|
||||
DN_STR_FMT(file_name), // file name
|
||||
call_site.line); // line number
|
||||
DN_USize header_size_no_ansi_codes = header.size - colour_esc.size - DN_Print_ESCResetStr8.size;
|
||||
|
||||
// NOTE: Header padding ////////////////////////////////////////////////////////////////////////
|
||||
DN_LOCAL_PERSIST DN_USize max_header_length = 0;
|
||||
max_header_length = DN_MAX(max_header_length, header_size_no_ansi_codes);
|
||||
DN_USize header_padding = max_header_length - header_size_no_ansi_codes;
|
||||
|
||||
// NOTE: Construct final log ///////////////////////////////////////////////////////////////////
|
||||
DN_Str8 user_msg = DN_Str8_InitFV(arena, fmt, args);
|
||||
DN_Str8 result = DN_Str8_Alloc(arena, header.size + header_padding + user_msg.size, DN_ZeroMem_No);
|
||||
if (DN_Str8_HasData(result)) {
|
||||
DN_MEMCPY(result.data, header.data, header.size);
|
||||
DN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
if (DN_Str8_HasData(user_msg))
|
||||
DN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_FILE_SCOPE void DN_Log_FVDefault_(DN_Str8 type, int log_type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Core *core = g_dn_core;
|
||||
(void)log_type;
|
||||
(void)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested //////////////////////////
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
if (core->log_to_file && !core->log_file.handle && !core->log_file.error) {
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 log_path = DN_OS_PathF(tmem.arena, "%.*s/dn.log", DN_STR_FMT(core->exe_dir));
|
||||
core->log_file = DN_OS_FileOpen(log_path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_AppendOnly, nullptr);
|
||||
}
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
|
||||
// NOTE: Generate the log header ///////////////////////////////////////////
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 log_line = DN_Log_MakeStr8(tmem.arena, !core->log_no_colour, type, log_type, call_site, fmt, args);
|
||||
|
||||
// NOTE: Print log /////////////////////////////////////////////////////////
|
||||
DN_Print_StdLn(log_type == DN_LogType_Error ? DN_PrintStd_Err : DN_PrintStd_Out, log_line);
|
||||
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
DN_OS_FileWrite(&core->log_file, log_line, nullptr);
|
||||
DN_OS_FileWrite(&core->log_file, DN_STR8("\n"), nullptr);
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
}
|
||||
|
||||
DN_API void DN_Log_FVCallSite(DN_Str8 type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (g_dn_core) {
|
||||
DN_LogProc *logging_function = g_dn_core->log_callback ? g_dn_core->log_callback : DN_Log_FVDefault_;
|
||||
logging_function(type, -1 /*log_type*/, g_dn_core->log_user_data, call_site, fmt, args);
|
||||
} else {
|
||||
// NOTE: Rarely taken branch, only when trying to use this library without initialising it
|
||||
DN_Print_StdLnFV(DN_PrintStd_Out, fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_Log_FCallSite(DN_Str8 type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Log_FVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_Log_TypeFVCallSite(DN_LogType type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 type_string = DN_STR8("DN-BAD-LOG-TYPE");
|
||||
switch (type) {
|
||||
case DN_LogType_Error: type_string = DN_STR8("ERROR"); break;
|
||||
case DN_LogType_Info: type_string = DN_STR8("INFO"); break;
|
||||
case DN_LogType_Warning: type_string = DN_STR8("WARN"); break;
|
||||
case DN_LogType_Debug: type_string = DN_STR8("DEBUG"); break;
|
||||
case DN_LogType_Count: type_string = DN_STR8("BADXX"); break;
|
||||
}
|
||||
DN_Log_FVCallSite(type_string, call_site, fmt, args);
|
||||
}
|
||||
|
||||
DN_API void DN_Log_TypeFCallSite(DN_LogType type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Log_TypeFVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// NOTE: [$ERRS] DN_ErrSink /////////////////////////////////////////////////////////////////////
|
||||
DN_FILE_SCOPE void DN_ErrSink_Check_(DN_ErrSink const *err)
|
||||
{
|
||||
DN_ASSERTF(err->arena, "Arena should be assigned in TLS init");
|
||||
if (!err->stack)
|
||||
return;
|
||||
|
||||
DN_ErrSinkNode *node = err->stack;
|
||||
DN_ASSERT(node->mode >= DN_ErrSinkMode_Nil && node->mode <= DN_ErrSinkMode_ExitOnError);
|
||||
DN_ASSERTF(node->next != node, "Error nodes cannot point to themselves");
|
||||
DN_ASSERT(node->msg_sentinel);
|
||||
|
||||
// NOTE: Walk the list ensuring we eventually terminate at the sentinel (e.g. we have a
|
||||
// well formed doubly-linked-list terminated by a sentinel, or otherwise we will hit the
|
||||
// walk limit or dereference a null pointer and assert)
|
||||
size_t WALK_LIMIT = 99'999;
|
||||
size_t walk = 0;
|
||||
for (DN_ErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next, walk++) {
|
||||
DN_ASSERTF(it, "Encountered null pointer which should not happen in a sentinel DLL");
|
||||
DN_ASSERT(walk < WALK_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_ErrSink *DN_ErrSink_Begin(DN_ErrSinkMode mode)
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_ErrSink *result = &tls->err_sink;
|
||||
DN_USize arena_pos = DN_Arena_Pos(result->arena);
|
||||
DN_ErrSinkNode *node = DN_Arena_New(result->arena, DN_ErrSinkNode, DN_ZeroMem_Yes);
|
||||
if (node) {
|
||||
node->next = result->stack;
|
||||
node->arena_pos = arena_pos;
|
||||
node->mode = mode;
|
||||
DN_SentinelDLL_InitArena(node->msg_sentinel, DN_ErrSinkMsg, result->arena);
|
||||
|
||||
// NOTE: Put the node at the front of the stack (e.g. sorted newest to oldest)
|
||||
result->stack = node;
|
||||
result->debug_open_close_counter++;
|
||||
}
|
||||
|
||||
// NOTE: Handle allocation error
|
||||
if (!DN_CHECK(node && node->msg_sentinel))
|
||||
DN_Arena_PopTo(result->arena, arena_pos);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ErrSink_HasError(DN_ErrSink *err)
|
||||
{
|
||||
bool result = err && DN_SentinelDLL_HasItems(err->stack->msg_sentinel);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ErrSinkMsg *DN_ErrSink_End(DN_Arena *arena, DN_ErrSink *err)
|
||||
{
|
||||
DN_ErrSinkMsg *result = nullptr;
|
||||
DN_ErrSink_Check_(err);
|
||||
if (!err || !err->stack)
|
||||
return result;
|
||||
|
||||
DN_ASSERTF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_ErrSinkNode *node = err->stack;
|
||||
DN_ErrSinkMsg *prev = nullptr;
|
||||
for (DN_ErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next) {
|
||||
DN_ErrSinkMsg *entry = DN_Arena_New(arena, DN_ErrSinkMsg, DN_ZeroMem_Yes);
|
||||
entry->msg = DN_Str8_Copy(arena, it->msg);
|
||||
entry->call_site = it->call_site;
|
||||
entry->error_code = it->error_code;
|
||||
if (!result)
|
||||
result = entry; // Assign first entry if we haven't yet
|
||||
if (prev)
|
||||
prev->next = entry; // Link the prev message to the current one
|
||||
prev = entry; // Update prev to latest
|
||||
}
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
err->stack = err->stack->next;
|
||||
DN_ASSERT(err->debug_open_close_counter);
|
||||
err->debug_open_close_counter--;
|
||||
DN_Arena_PopTo(err->arena, node->arena_pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_ErrSink_AddMsgToStr8Builder_(DN_Str8Builder *builder, DN_ErrSinkMsg *msg, DN_ErrSinkMsg *end)
|
||||
{
|
||||
if (msg == end) // NOTE: No error messages to add
|
||||
return;
|
||||
|
||||
if (msg->next == end) {
|
||||
DN_ErrSinkMsg *it = msg;
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%.*s:%05I32u:%.*s %.*s",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_STR_FMT(it->msg));
|
||||
} else {
|
||||
// NOTE: More than one message
|
||||
for (DN_ErrSinkMsg *it = msg; it != end; it = it->next) {
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%s - %.*s:%05I32u:%.*s%s%.*s",
|
||||
it == msg ? "" : "\n",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_Str8_HasData(it->msg) ? " " : "",
|
||||
DN_STR_FMT(it->msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_ErrSink_EndStr8(DN_Arena *arena, DN_ErrSink *err)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
DN_ErrSink_Check_(err);
|
||||
if (!err || !err->stack)
|
||||
return result;
|
||||
|
||||
DN_ASSERTF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_TLSTMem tmem = DN_TLS_PushTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init_TLS();
|
||||
DN_ErrSink_AddMsgToStr8Builder_(&builder, err->stack->msg_sentinel->next, err->stack->msg_sentinel);
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
uint64_t arena_pos = err->stack->arena_pos;
|
||||
err->stack = err->stack->next;
|
||||
DN_Arena_PopTo(err->arena, arena_pos);
|
||||
DN_ASSERT(err->debug_open_close_counter);
|
||||
err->debug_open_close_counter--;
|
||||
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_ErrSink_EndAndIgnore(DN_ErrSink *err)
|
||||
{
|
||||
DN_ErrSink_End(nullptr, err);
|
||||
}
|
||||
|
||||
DN_API bool DN_ErrSink_EndAndLogError_(DN_ErrSink *err, DN_CallSite call_site, DN_Str8 err_msg)
|
||||
{
|
||||
DN_ASSERTF(err->stack && err->stack->msg_sentinel, "Begin must be called before calling end");
|
||||
DN_TLSTMem tmem = DN_TLS_PushTMem(nullptr);
|
||||
DN_ErrSinkMode mode = err->stack->mode;
|
||||
DN_ErrSinkMsg *msg = DN_ErrSink_End(tmem.arena, err);
|
||||
if (!msg)
|
||||
return false;
|
||||
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init_TLS();
|
||||
if (DN_Str8_HasData(err_msg)) {
|
||||
DN_Str8Builder_AppendRef(&builder, err_msg);
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8(":"));
|
||||
} else {
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("Error(s) encountered:"));
|
||||
}
|
||||
|
||||
if (msg->next) // NOTE: More than 1 message
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("\n"));
|
||||
DN_ErrSink_AddMsgToStr8Builder_(&builder, msg, nullptr);
|
||||
|
||||
DN_Str8 log = DN_Str8Builder_Build_TLS(&builder);
|
||||
DN_Log_TypeFCallSite(DN_LogType_Error, call_site, "%.*s", DN_STR_FMT(log));
|
||||
|
||||
if (mode == DN_ErrSinkMode_DebugBreakOnEndAndLog) {
|
||||
DN_DEBUG_BREAK;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_ErrSink_EndAndLogErrorFV_(DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_ErrSink_EndAndLogError_(err, call_site, log);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ErrSink_EndAndLogErrorF_(DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_ErrSink_EndAndLogError_(err, call_site, log);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_ErrSink_EndAndExitIfErrorFV_(DN_ErrSink *err, DN_CallSite call_site, uint32_t exit_val, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (DN_ErrSink_EndAndLogErrorFV_(err, call_site, fmt, args)) {
|
||||
DN_DEBUG_BREAK;
|
||||
DN_OS_Exit(exit_val);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_ErrSink_EndAndExitIfErrorF_(DN_ErrSink *err, DN_CallSite call_site, uint32_t exit_val, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_ErrSink_EndAndExitIfErrorFV_(err, call_site, exit_val, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_ErrSink_AppendFV_(DN_ErrSink *err, uint32_t error_code, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
DN_ErrSinkNode *node = err->stack;
|
||||
DN_ASSERTF(node, "Error sink must be begun by calling 'Begin' before using this function.");
|
||||
|
||||
DN_ErrSinkMsg *msg = DN_Arena_New(err->arena, DN_ErrSinkMsg, DN_ZeroMem_Yes);
|
||||
if (DN_CHECK(msg)) {
|
||||
msg->msg = DN_Str8_InitFV(err->arena, fmt, args);
|
||||
msg->error_code = error_code;
|
||||
msg->call_site = DN_TLS_Get()->call_site;
|
||||
DN_SentinelDLL_Prepend(node->msg_sentinel, msg);
|
||||
|
||||
if (node->mode == DN_ErrSinkMode_ExitOnError)
|
||||
DN_ErrSink_EndAndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_ErrSink_AppendF_(DN_ErrSink *err, uint32_t error_code, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_ErrSink_AppendFV_(err, error_code, fmt, args);
|
||||
va_end(args);
|
||||
}
|
937
dqn_base.h
937
dqn_base.h
@ -1,937 +0,0 @@
|
||||
/*
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
// $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$$$$$$\ | \____$$\ $$ _____|$$ __$$\
|
||||
// $$ __$$\ $$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ | $$ |$$ __$$ | \____$$\ $$ ____|
|
||||
// $$$$$$$ |\$$$$$$$ |$$$$$$$ |\$$$$$$$\
|
||||
// \_______/ \_______|\_______/ \_______|
|
||||
//
|
||||
// dqn_base.h -- Base primitives for the library
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$MACR] Macros -- General macros
|
||||
// [$TYPE] Types -- Basic types and typedefs
|
||||
// [$SDLL] DN_SentinelDLL -- Doubly linked list w/ sentinel macros
|
||||
// [$INTR] Intrinsics -- Platform agnostic functions for CPU instructions (e.g. atomics, cpuid, ...)
|
||||
// [$CALL] DN_CallSite -- Source code location/tracing
|
||||
// [$TMUT] DN_TicketMutex -- Userland mutex via spinlocking atomics
|
||||
// [$PRIN] DN_Print -- Console printing
|
||||
// [$LLOG] DN_Log -- Console logging macros
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$MACR] Macros ////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_STRINGIFY(x) #x
|
||||
#define DN_TOKEN_COMBINE2(x, y) x ## y
|
||||
#define DN_TOKEN_COMBINE(x, y) DN_TOKEN_COMBINE2(x, y)
|
||||
|
||||
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__clang__)
|
||||
#define DN_COMPILER_CLANG_CL
|
||||
#define DN_COMPILER_CLANG
|
||||
#else
|
||||
#define DN_COMPILER_MSVC
|
||||
#endif
|
||||
#elif defined(__clang__)
|
||||
#define DN_COMPILER_CLANG
|
||||
#elif defined(__GNUC__)
|
||||
#define DN_COMPILER_GCC
|
||||
#endif
|
||||
|
||||
// NOTE: Declare struct literals that work in both C and C++ because the syntax
|
||||
// is different between languages.
|
||||
#if 0
|
||||
struct Foo { int a; }
|
||||
struct Foo foo = DN_LITERAL(Foo){32}; // Works on both C and C++
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define DN_LITERAL(T) T
|
||||
#else
|
||||
#define DN_LITERAL(T) (T)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define DN_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define DN_THREAD_LOCAL _Thread_local
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DN_OS_WIN32
|
||||
#elif defined(__gnu_linux__) || defined(__linux__)
|
||||
#define DN_OS_UNIX
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h> // PRIu64...
|
||||
|
||||
#if !defined(DN_OS_WIN32)
|
||||
#include <stdlib.h> // exit()
|
||||
#endif
|
||||
|
||||
#if !defined(DN_PLATFORM_EMSCRIPTEN) && \
|
||||
!defined(DN_PLATFORM_POSIX) && \
|
||||
!defined(DN_PLATFORM_WIN32)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define DN_PLATFORM_ARM64
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define DN_PLATFORM_EMSCRIPTEN
|
||||
#elif defined(DN_OS_WIN32)
|
||||
#define DN_PLATFORM_WIN32
|
||||
#else
|
||||
#define DN_PLATFORM_POSIX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#if defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED
|
||||
#else
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DN_COMPILER_MSVC)
|
||||
#define DN_FMT_ATTRIB _Printf_format_string_
|
||||
#define DN_MSVC_WARNING_PUSH __pragma(warning(push))
|
||||
#define DN_MSVC_WARNING_DISABLE(...) __pragma(warning(disable: ##__VA_ARGS__))
|
||||
#define DN_MSVC_WARNING_POP __pragma(warning(pop))
|
||||
#else
|
||||
#define DN_FMT_ATTRIB
|
||||
#define DN_MSVC_WARNING_PUSH
|
||||
#define DN_MSVC_WARNING_DISABLE(...)
|
||||
#define DN_MSVC_WARNING_POP
|
||||
#endif
|
||||
|
||||
#if defined(DN_COMPILER_CLANG) || defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_GCC_WARNING_PUSH _Pragma("GCC diagnostic push")
|
||||
#define DN_GCC_WARNING_DISABLE_HELPER_0(x) #x
|
||||
#define DN_GCC_WARNING_DISABLE_HELPER_1(y) DN_GCC_WARNING_DISABLE_HELPER_0(GCC diagnostic ignored #y)
|
||||
#define DN_GCC_WARNING_DISABLE(warning) _Pragma(DN_GCC_WARNING_DISABLE_HELPER_1(warning))
|
||||
#define DN_GCC_WARNING_POP _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DN_GCC_WARNING_PUSH
|
||||
#define DN_GCC_WARNING_DISABLE(...)
|
||||
#define DN_GCC_WARNING_POP
|
||||
#endif
|
||||
|
||||
// NOTE: MSVC does not support the feature detection macro for instance so we
|
||||
// compile it out
|
||||
#if defined(__has_feature)
|
||||
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
||||
#else
|
||||
#define DN_HAS_FEATURE(expr) 0
|
||||
#endif
|
||||
|
||||
#define DN_FOR_UINDEX(index, size) for (DN_USize index = 0; index < size; index++)
|
||||
#define DN_FOR_IINDEX(index, size) for (DN_isize index = 0; index < size; index++)
|
||||
|
||||
#define DN_ForItSize(it, T, array, size) for (struct { USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data++)
|
||||
#define DN_ForIt(it, T, array) for (struct { USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data++)
|
||||
#define DN_ForItCArray(it, T, array) for (struct { USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ARRAY_UCOUNT(array); it.index++, it.data++)
|
||||
|
||||
#define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
|
||||
#define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
|
||||
#define DN_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
|
||||
#define DN_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
|
||||
|
||||
// NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_MEMCPY) || !defined(DN_MEMSET) || !defined(DN_MEMCMP) || !defined(DN_MEMMOVE)
|
||||
#include <string.h>
|
||||
#if !defined(DN_MEMCPY)
|
||||
#define DN_MEMCPY(dest, src, count) memcpy((dest), (src), (count))
|
||||
#endif
|
||||
#if !defined(DN_MEMSET)
|
||||
#define DN_MEMSET(dest, value, count) memset((dest), (value), (count))
|
||||
#endif
|
||||
#if !defined(DN_MEMCMP)
|
||||
#define DN_MEMCMP(lhs, rhs, count) memcmp((lhs), (rhs), (count))
|
||||
#endif
|
||||
#if !defined(DN_MEMMOVE)
|
||||
#define DN_MEMMOVE(dest, src, count) memmove((dest), (src), (count))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_SQRTF) || !defined(DN_SINF) || !defined(DN_COSF) || !defined(DN_TANF)
|
||||
#include <math.h>
|
||||
#if !defined(DN_SQRTF)
|
||||
#define DN_SQRTF(val) sqrtf(val)
|
||||
#endif
|
||||
#if !defined(DN_SINF)
|
||||
#define DN_SINF(val) sinf(val)
|
||||
#endif
|
||||
#if !defined(DN_COSF)
|
||||
#define DN_COSF(val) cosf(val)
|
||||
#endif
|
||||
#if !defined(DN_TANF)
|
||||
#define DN_TANF(val) tanf(val)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_PI 3.14159265359f
|
||||
|
||||
#define DN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DN_PI / 180.0f))
|
||||
#define DN_RADIAN_TO_DEGREE(radians) ((radians) * (180.f * DN_PI))
|
||||
|
||||
#define DN_ABS(val) (((val) < 0) ? (-(val)) : (val))
|
||||
#define DN_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define DN_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define DN_CLAMP(val, lo, hi) DN_MAX(DN_MIN(val, hi), lo)
|
||||
#define DN_SQUARED(val) ((val) * (val))
|
||||
|
||||
#define DN_SWAP(a, b) \
|
||||
do \
|
||||
{ \
|
||||
auto temp = a; \
|
||||
a = b; \
|
||||
b = temp; \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
|
||||
#if defined(DN_STATIC_API)
|
||||
#define DN_API static
|
||||
#else
|
||||
#define DN_API
|
||||
#endif
|
||||
|
||||
#define DN_LOCAL_PERSIST static
|
||||
#define DN_FILE_SCOPE static
|
||||
#define DN_CAST(val) (val)
|
||||
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define DN_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
// NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_ISIZEOF(val) DN_CAST(ptrdiff_t)sizeof(val)
|
||||
#define DN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0])))
|
||||
#define DN_ARRAY_ICOUNT(array) (DN_ISize)DN_ARRAY_UCOUNT(array)
|
||||
#define DN_CHAR_COUNT(string) (sizeof(string) - 1)
|
||||
|
||||
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_BYTES(val) ((uint64_t)val)
|
||||
#define DN_KILOBYTES(val) ((uint64_t)1024 * DN_BYTES(val))
|
||||
#define DN_MEGABYTES(val) ((uint64_t)1024 * DN_KILOBYTES(val))
|
||||
#define DN_GIGABYTES(val) ((uint64_t)1024 * DN_MEGABYTES(val))
|
||||
|
||||
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_SECONDS_TO_MS(val) ((val) * 1000)
|
||||
#define DN_MINS_TO_S(val) ((val) * 60ULL)
|
||||
#define DN_HOURS_TO_S(val) (DN_MINS_TO_S(val) * 60ULL)
|
||||
#define DN_DAYS_TO_S(val) (DN_HOURS_TO_S(val) * 24ULL)
|
||||
#define DN_WEEKS_TO_S(val) (DN_DAYS_TO_S(val) * 7ULL)
|
||||
#define DN_YEARS_TO_S(val) (DN_WEEKS_TO_S(val) * 52ULL)
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||
#else
|
||||
#define DN_HAS_BUILTIN(expr) 0
|
||||
#endif
|
||||
|
||||
// NOTE: Debug Break ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_DEBUG_BREAK)
|
||||
#if defined(NDEBUG)
|
||||
#define DN_DEBUG_BREAK
|
||||
#else
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#define DN_DEBUG_BREAK __debugbreak()
|
||||
#elif DN_HAS_BUILTIN(__builtin_debugtrap)
|
||||
#define DN_DEBUG_BREAK __builtin_debugtrap()
|
||||
#elif DN_HAS_BUILTIN(__builtin_trap) || defined(DN_COMPILER_GCC)
|
||||
#define DN_DEBUG_BREAK __builtin_trap()
|
||||
#else
|
||||
#include <signal.h>
|
||||
#if defined(SIGTRAP)
|
||||
#define DN_DEBUG_BREAK raise(SIGTRAP)
|
||||
#else
|
||||
#define DN_DEBUG_BREAK raise(SIGABRT)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Assert Macros /////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_HARD_ASSERT(expr) DN_HARD_ASSERTF(expr, "")
|
||||
#define DN_HARD_ASSERTF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_Log_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DN_DEBUG_BREAK; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(DN_NO_ASSERT)
|
||||
#define DN_ASSERT(...)
|
||||
#define DN_ASSERT_ONCE(...)
|
||||
#define DN_ASSERTF(...)
|
||||
#define DN_ASSERTF_ONCE(...)
|
||||
#else
|
||||
#define DN_ASSERT(expr) DN_ASSERTF((expr), "")
|
||||
#define DN_ASSERT_ONCE(expr) DN_ASSERTF_ONCE((expr), "")
|
||||
|
||||
#define DN_ASSERTF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_Log_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DN_DEBUG_BREAK; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_ASSERTF_ONCE(expr, fmt, ...) \
|
||||
do { \
|
||||
static bool once = true; \
|
||||
if (!(expr) && once) { \
|
||||
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
|
||||
DN_Log_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
once = false; \
|
||||
DN_DEBUG_BREAK; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define DN_INVALID_CODE_PATHF(fmt, ...) DN_HARD_ASSERTF(0, fmt, ##__VA_ARGS__)
|
||||
#define DN_INVALID_CODE_PATH DN_INVALID_CODE_PATHF("Invalid code path triggered")
|
||||
|
||||
// NOTE: Check macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_CHECK(expr) DN_CHECKF(expr, "")
|
||||
|
||||
#if defined(DN_NO_CHECK_BREAK)
|
||||
#define DN_CHECKF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_Log_TypeFCallSite(DN_LogType_Warning, DN_CALL_SITE, fmt, ## __VA_ARGS__), false))
|
||||
#else
|
||||
#define DN_CHECKF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_Log_TypeFCallSite(DN_LogType_Error, DN_CALL_SITE, fmt, ## __VA_ARGS__), DN_StackTrace_Print(128 /*limit*/), DN_DEBUG_BREAK, false))
|
||||
#endif
|
||||
|
||||
// NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
#define DN_ZERO_INIT {}
|
||||
#else
|
||||
#define DN_ZERO_INIT {0}
|
||||
#endif
|
||||
|
||||
// NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
template <typename Procedure>
|
||||
struct DN_Defer
|
||||
{
|
||||
Procedure proc;
|
||||
DN_Defer(Procedure p) : proc(p) {}
|
||||
~DN_Defer() { proc(); }
|
||||
};
|
||||
|
||||
struct DN_DeferHelper
|
||||
{
|
||||
template <typename Lambda>
|
||||
DN_Defer<Lambda> operator+(Lambda lambda) { return DN_Defer<Lambda>(lambda); };
|
||||
};
|
||||
|
||||
#define DN_UNIQUE_NAME(prefix) DN_TOKEN_COMBINE(prefix, __LINE__)
|
||||
#define DN_DEFER const auto DN_UNIQUE_NAME(defer_lambda_) = DN_DeferHelper() + [&]()
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#define DN_DEFER_LOOP(begin, end) \
|
||||
for (bool DN_UNIQUE_NAME(once) = (begin, true); \
|
||||
DN_UNIQUE_NAME(once); \
|
||||
end, DN_UNIQUE_NAME(once) = false)
|
||||
|
||||
// NOTE: [$TYPE] Types /////////////////////////////////////////////////////////////////////////////
|
||||
typedef intptr_t DN_ISize;
|
||||
typedef uintptr_t DN_USize;
|
||||
|
||||
typedef int8_t DN_I8;
|
||||
typedef int16_t DN_I16;
|
||||
typedef int32_t DN_I32;
|
||||
typedef int64_t DN_I64;
|
||||
|
||||
typedef uint8_t DN_U8;
|
||||
typedef uint16_t DN_U16;
|
||||
typedef uint32_t DN_U32;
|
||||
typedef uint64_t DN_U64;
|
||||
|
||||
typedef float DN_F32;
|
||||
typedef double DN_F64;
|
||||
typedef unsigned int DN_UInt;
|
||||
typedef int32_t DN_B32;
|
||||
|
||||
#define DN_F32_MAX 3.402823466e+38F
|
||||
#define DN_F32_MIN 1.175494351e-38F
|
||||
#define DN_F64_MAX 1.7976931348623158e+308
|
||||
#define DN_F64_MIN 2.2250738585072014e-308
|
||||
#define DN_USIZE_MAX UINTPTR_MAX
|
||||
#define DN_ISIZE_MAX INTPTR_MAX
|
||||
#define DN_ISIZE_MIN INTPTR_MIN
|
||||
|
||||
enum DN_ZeroMem
|
||||
{
|
||||
DN_ZeroMem_No, // Memory can be handed out without zero-ing it out
|
||||
DN_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee
|
||||
};
|
||||
|
||||
struct DN_Str8
|
||||
{
|
||||
char *data; // The bytes of the string
|
||||
DN_USize size; // The number of bytes in the string
|
||||
|
||||
char const *begin() const { return data; }
|
||||
char const *end () const { return data + size; }
|
||||
char *begin() { return data; }
|
||||
char *end () { return data + size; }
|
||||
};
|
||||
|
||||
template <typename T> struct DN_Slice // A pointer and length container of data
|
||||
{
|
||||
T *data;
|
||||
DN_USize size;
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
// NOTE: [$CALL] DN_CallSite //////////////////////////////////////////////////////////////////////
|
||||
struct DN_CallSite
|
||||
{
|
||||
DN_Str8 file;
|
||||
DN_Str8 function;
|
||||
uint32_t line;
|
||||
};
|
||||
#define DN_CALL_SITE DN_CallSite{DN_STR8(__FILE__), DN_STR8(__func__), __LINE__}
|
||||
|
||||
// NOTE: [$ErrS] DN_ErrSink /////////////////////////////////////////////////////////////////////
|
||||
enum DN_ErrSinkMode
|
||||
{
|
||||
// Default behaviour to accumulate errors into the sink
|
||||
DN_ErrSinkMode_Nil,
|
||||
|
||||
// Break into the debugger (int3) when error is encountered and the sink is
|
||||
// ended by the 'end and log' functions.
|
||||
DN_ErrSinkMode_DebugBreakOnEndAndLog,
|
||||
|
||||
// When an error is encountered, exit the program with the error code of the
|
||||
// error that was caught.
|
||||
DN_ErrSinkMode_ExitOnError,
|
||||
};
|
||||
|
||||
struct DN_ErrSinkMsg
|
||||
{
|
||||
int32_t error_code;
|
||||
DN_Str8 msg;
|
||||
DN_CallSite call_site;
|
||||
DN_ErrSinkMsg *next;
|
||||
DN_ErrSinkMsg *prev;
|
||||
};
|
||||
|
||||
struct DN_ErrSinkNode
|
||||
{
|
||||
DN_ErrSinkMode mode; // Controls how the sink behaves when an error is registered onto the sink.
|
||||
DN_ErrSinkMsg *msg_sentinel; // List of error messages accumulated for the current scope
|
||||
DN_ErrSinkNode *next; // Next error scope
|
||||
uint64_t arena_pos; // Position to reset the arena when the scope is ended
|
||||
};
|
||||
|
||||
struct DN_ErrSink
|
||||
{
|
||||
// Arena solely for handling errors take from the thread's TLS
|
||||
struct DN_Arena *arena;
|
||||
|
||||
// Each entry in the stack represents the errors accumulated in the scope
|
||||
// between a begin and end scope of the stink. The base sink is stored in
|
||||
// the thread's TLS.
|
||||
//
|
||||
// The stack has the latest error scope at the front.
|
||||
DN_ErrSinkNode *stack;
|
||||
|
||||
size_t debug_open_close_counter;
|
||||
};
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: DN_Atomic_Add/Exchange return the previous value store in the target
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
#include <intrin.h>
|
||||
#define DN_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
|
||||
#define DN_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
|
||||
#define DN_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
|
||||
#define DN_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
|
||||
#define DN_Atomic_SubU32(target, value) DN_Atomic_AddU32(DN_CAST(long volatile *)target, (long)-value)
|
||||
#define DN_Atomic_SubU64(target, value) DN_Atomic_AddU64(target, (uint64_t)-value)
|
||||
|
||||
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
|
||||
|
||||
#define DN_CPU_TSC() __rdtsc()
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
#if defined(__ANDROID__)
|
||||
#elif defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emmintrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#define DN_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
#define DN_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
|
||||
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
|
||||
#if defined(DN_COMPILER_GCC)
|
||||
#define DN_CPU_TSC() __rdtsc()
|
||||
#else
|
||||
#define DN_CPU_TSC() __builtin_readcyclecounter()
|
||||
#endif
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence
|
||||
#else
|
||||
#define DN_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory")
|
||||
#define DN_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory")
|
||||
#endif
|
||||
#else
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
|
||||
#if !defined(DN_PLATFORM_ARM64)
|
||||
struct DN_CPURegisters
|
||||
{
|
||||
int eax;
|
||||
int ebx;
|
||||
int ecx;
|
||||
int edx;
|
||||
};
|
||||
|
||||
union DN_CPUIDResult
|
||||
{
|
||||
DN_CPURegisters reg;
|
||||
int values[4];
|
||||
};
|
||||
|
||||
struct DN_CPUIDArgs
|
||||
{
|
||||
int eax;
|
||||
int ecx;
|
||||
};
|
||||
|
||||
#define DN_CPU_FEAT_XMACRO \
|
||||
DN_CPU_FEAT_XENTRY(3DNow) \
|
||||
DN_CPU_FEAT_XENTRY(3DNowExt) \
|
||||
DN_CPU_FEAT_XENTRY(ABM) \
|
||||
DN_CPU_FEAT_XENTRY(AES) \
|
||||
DN_CPU_FEAT_XENTRY(AVX) \
|
||||
DN_CPU_FEAT_XENTRY(AVX2) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512F) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512DQ) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512IFMA) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512PF) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512ER) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512CD) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512BW) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VL) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VBMI) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VBMI2) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VNNI) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512BITALG) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VPOPCNTDQ) \
|
||||
DN_CPU_FEAT_XENTRY(AVX5124VNNIW) \
|
||||
DN_CPU_FEAT_XENTRY(AVX5124FMAPS) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512VP2INTERSECT) \
|
||||
DN_CPU_FEAT_XENTRY(AVX512FP16) \
|
||||
DN_CPU_FEAT_XENTRY(CLZERO) \
|
||||
DN_CPU_FEAT_XENTRY(CMPXCHG8B) \
|
||||
DN_CPU_FEAT_XENTRY(CMPXCHG16B) \
|
||||
DN_CPU_FEAT_XENTRY(F16C) \
|
||||
DN_CPU_FEAT_XENTRY(FMA) \
|
||||
DN_CPU_FEAT_XENTRY(FMA4) \
|
||||
DN_CPU_FEAT_XENTRY(FP128) \
|
||||
DN_CPU_FEAT_XENTRY(FP256) \
|
||||
DN_CPU_FEAT_XENTRY(FPU) \
|
||||
DN_CPU_FEAT_XENTRY(MMX) \
|
||||
DN_CPU_FEAT_XENTRY(MONITOR) \
|
||||
DN_CPU_FEAT_XENTRY(MOVBE) \
|
||||
DN_CPU_FEAT_XENTRY(MOVU) \
|
||||
DN_CPU_FEAT_XENTRY(MmxExt) \
|
||||
DN_CPU_FEAT_XENTRY(PCLMULQDQ) \
|
||||
DN_CPU_FEAT_XENTRY(POPCNT) \
|
||||
DN_CPU_FEAT_XENTRY(RDRAND) \
|
||||
DN_CPU_FEAT_XENTRY(RDSEED) \
|
||||
DN_CPU_FEAT_XENTRY(RDTSCP) \
|
||||
DN_CPU_FEAT_XENTRY(SHA) \
|
||||
DN_CPU_FEAT_XENTRY(SSE) \
|
||||
DN_CPU_FEAT_XENTRY(SSE2) \
|
||||
DN_CPU_FEAT_XENTRY(SSE3) \
|
||||
DN_CPU_FEAT_XENTRY(SSE41) \
|
||||
DN_CPU_FEAT_XENTRY(SSE42) \
|
||||
DN_CPU_FEAT_XENTRY(SSE4A) \
|
||||
DN_CPU_FEAT_XENTRY(SSSE3) \
|
||||
DN_CPU_FEAT_XENTRY(TSC) \
|
||||
DN_CPU_FEAT_XENTRY(TscInvariant) \
|
||||
DN_CPU_FEAT_XENTRY(VAES) \
|
||||
DN_CPU_FEAT_XENTRY(VPCMULQDQ)
|
||||
|
||||
enum DN_CPUFeature
|
||||
{
|
||||
#define DN_CPU_FEAT_XENTRY(label) DN_CPUFeature_##label,
|
||||
DN_CPU_FEAT_XMACRO
|
||||
#undef DN_CPU_FEAT_XENTRY
|
||||
DN_CPUFeature_Count,
|
||||
};
|
||||
|
||||
struct DN_CPUFeatureDecl
|
||||
{
|
||||
DN_CPUFeature value;
|
||||
DN_Str8 label;
|
||||
};
|
||||
|
||||
struct DN_CPUFeatureQuery
|
||||
{
|
||||
DN_CPUFeature feature;
|
||||
bool available;
|
||||
};
|
||||
|
||||
struct DN_CPUReport
|
||||
{
|
||||
char vendor [4 /*bytes*/ * 3 /*EDX, ECX, EBX*/ + 1 /*null*/];
|
||||
char brand [48];
|
||||
uint64_t features[(DN_CPUFeature_Count / (sizeof(uint64_t) * 8)) + 1];
|
||||
};
|
||||
|
||||
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
||||
#endif // DN_PLATFORM_ARM64
|
||||
|
||||
// NOTE: [$TMUT] DN_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
struct DN_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
|
||||
};
|
||||
|
||||
// NOTE: [$MUTX] DN_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSMutex
|
||||
{
|
||||
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
char win32_handle[48];
|
||||
#else
|
||||
pthread_mutex_t posix_handle;
|
||||
pthread_mutexattr_t posix_attribs;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$PRIN] DN_Print /////////////////////////////////////////////////////////////////////////
|
||||
enum DN_PrintStd
|
||||
{
|
||||
DN_PrintStd_Out,
|
||||
DN_PrintStd_Err,
|
||||
};
|
||||
|
||||
enum DN_PrintBold
|
||||
{
|
||||
DN_PrintBold_No,
|
||||
DN_PrintBold_Yes,
|
||||
};
|
||||
|
||||
struct DN_PrintStyle
|
||||
{
|
||||
DN_PrintBold bold;
|
||||
bool colour;
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
enum DN_PrintESCColour
|
||||
{
|
||||
DN_PrintESCColour_Fg,
|
||||
DN_PrintESCColour_Bg,
|
||||
};
|
||||
|
||||
|
||||
// NOTE: [$LLOG] DN_Log ///////////////////////////////////////////////////////////////////////////
|
||||
enum DN_LogType
|
||||
{
|
||||
DN_LogType_Debug,
|
||||
DN_LogType_Info,
|
||||
DN_LogType_Warning,
|
||||
DN_LogType_Error,
|
||||
DN_LogType_Count,
|
||||
};
|
||||
|
||||
typedef void DN_LogProc(DN_Str8 type,
|
||||
int log_type,
|
||||
void *user_data,
|
||||
DN_CallSite call_site,
|
||||
DN_FMT_ATTRIB char const *fmt,
|
||||
va_list va);
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
DN_FORCE_INLINE uint64_t DN_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value);
|
||||
DN_FORCE_INLINE long DN_Atomic_SetValue32 (long volatile *target, long value);
|
||||
#if !defined(DN_PLATFORM_ARM64)
|
||||
DN_API DN_CPUIDResult DN_CPU_ID (DN_CPUIDArgs args);
|
||||
DN_API DN_USize DN_CPU_HasFeatureArray (DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size);
|
||||
DN_API bool DN_CPU_HasFeature (DN_CPUReport const *report, DN_CPUFeature feature);
|
||||
DN_API bool DN_CPU_HasAllFeatures (DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size);
|
||||
template <DN_USize N> bool DN_CPU_HasAllFeaturesCArray (DN_CPUReport const *report, DN_CPUFeature const (&features)[N]);
|
||||
DN_API void DN_CPU_SetFeature (DN_CPUReport *report, DN_CPUFeature feature);
|
||||
DN_API DN_CPUReport DN_CPU_Report ();
|
||||
#endif
|
||||
|
||||
// NOTE: [$TMUT] DN_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_TicketMutex_Begin (DN_TicketMutex *mutex);
|
||||
DN_API void DN_TicketMutex_End (DN_TicketMutex *mutex);
|
||||
DN_API DN_UInt DN_TicketMutex_MakeTicket (DN_TicketMutex *mutex);
|
||||
DN_API void DN_TicketMutex_BeginTicket (DN_TicketMutex const *mutex, DN_UInt ticket);
|
||||
DN_API bool DN_TicketMutex_CanLock (DN_TicketMutex const *mutex, DN_UInt ticket);
|
||||
|
||||
// NOTE: [$PRIN] DN_Print /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Print Style ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_PrintStyle DN_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, DN_PrintBold bold);
|
||||
DN_API DN_PrintStyle DN_Print_StyleColourU32 (uint32_t rgb, DN_PrintBold bold);
|
||||
DN_API DN_PrintStyle DN_Print_StyleBold ();
|
||||
|
||||
// NOTE: Print Macros //////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_Print(string) DN_Print_Std(DN_PrintStd_Out, string)
|
||||
#define DN_Print_F(fmt, ...) DN_Print_StdF(DN_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_FV(fmt, args) DN_Print_StdFV(DN_PrintStd_Out, fmt, args)
|
||||
|
||||
#define DN_Print_Style(style, string) DN_Print_StdStyle(DN_PrintStd_Out, style, string)
|
||||
#define DN_Print_FStyle(style, fmt, ...) DN_Print_StdFStyle(DN_PrintStd_Out, style, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_FVStyle(style, fmt, args, ...) DN_Print_StdFVStyle(DN_PrintStd_Out, style, fmt, args)
|
||||
|
||||
#define DN_Print_Ln(string) DN_Print_StdLn(DN_PrintStd_Out, string)
|
||||
#define DN_Print_LnF(fmt, ...) DN_Print_StdLnF(DN_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_LnFV(fmt, args) DN_Print_StdLnFV(DN_PrintStd_Out, fmt, args)
|
||||
|
||||
#define DN_Print_LnStyle(style, string) DN_Print_StdLnStyle(DN_PrintStd_Out, style, string);
|
||||
#define DN_Print_LnFStyle(style, fmt, ...) DN_Print_StdLnFStyle(DN_PrintStd_Out, style, fmt, ## __VA_ARGS__);
|
||||
#define DN_Print_LnFVStyle(style, fmt, args) DN_Print_StdLnFVStyle(DN_PrintStd_Out, style, fmt, args);
|
||||
|
||||
#define DN_Print_Err(string) DN_Print_Std(DN_PrintStd_Err, string)
|
||||
#define DN_Print_ErrF(fmt, ...) DN_Print_StdF(DN_PrintStd_Err, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_ErrFV(fmt, args) DN_Print_StdFV(DN_PrintStd_Err, fmt, args)
|
||||
|
||||
#define DN_Print_ErrStyle(style, string) DN_Print_StdStyle(DN_PrintStd_Err, style, string)
|
||||
#define DN_Print_ErrFStyle(style, fmt, ...) DN_Print_StdFStyle(DN_PrintStd_Err, style, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_ErrFVStyle(style, fmt, args, ...) DN_Print_StdFVStyle(DN_PrintStd_Err, style, fmt, args)
|
||||
|
||||
#define DN_Print_ErrLn(string) DN_Print_StdLn(DN_PrintStd_Err, string)
|
||||
#define DN_Print_ErrLnF(fmt, ...) DN_Print_StdLnF(DN_PrintStd_Err, fmt, ## __VA_ARGS__)
|
||||
#define DN_Print_ErrLnFV(fmt, args) DN_Print_StdLnFV(DN_PrintStd_Err, fmt, args)
|
||||
|
||||
#define DN_Print_ErrLnStyle(style, string) DN_Print_StdLnStyle(DN_PrintStd_Err, style, string);
|
||||
#define DN_Print_ErrLnFStyle(style, fmt, ...) DN_Print_StdLnFStyle(DN_PrintStd_Err, style, fmt, ## __VA_ARGS__);
|
||||
#define DN_Print_ErrLnFVStyle(style, fmt, args) DN_Print_StdLnFVStyle(DN_PrintStd_Err, style, fmt, args);
|
||||
// NOTE: Print /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_Print_Std (DN_PrintStd std_handle, DN_Str8 string);
|
||||
DN_API void DN_Print_StdF (DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Print_StdFV (DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_Print_StdStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_Str8 string);
|
||||
DN_API void DN_Print_StdFStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Print_StdFVStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_Print_StdLn (DN_PrintStd std_handle, DN_Str8 string);
|
||||
DN_API void DN_Print_StdLnF (DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Print_StdLnFV (DN_PrintStd std_handle, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_Print_StdLnStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_Str8 string);
|
||||
DN_API void DN_Print_StdLnFStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Print_StdLnFVStyle (DN_PrintStd std_handle, DN_PrintStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
// NOTE: ANSI Formatting Codes /////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_Print_ESCColourStr8 (DN_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||
DN_API DN_Str8 DN_Print_ESCColourU32Str8 (DN_PrintESCColour colour, uint32_t value);
|
||||
|
||||
#define DN_Print_ESCColourFgStr8(r, g, b) DN_Print_ESCColourStr8(DN_PrintESCColour_Fg, r, g, b)
|
||||
#define DN_Print_ESCColourBgStr8(r, g, b) DN_Print_ESCColourStr8(DN_PrintESCColour_Bg, r, g, b)
|
||||
#define DN_Print_ESCColourFg(r, g, b) DN_Print_ESCColourStr8(DN_PrintESCColour_Fg, r, g, b).data
|
||||
#define DN_Print_ESCColourBg(r, g, b) DN_Print_ESCColourStr8(DN_PrintESCColour_Bg, r, g, b).data
|
||||
|
||||
#define DN_Print_ESCColourFgU32Str8(value) DN_Print_ESCColourU32Str8(DN_PrintESCColour_Fg, value)
|
||||
#define DN_Print_ESCColourBgU32Str8(value) DN_Print_ESCColourU32Str8(DN_PrintESCColour_Bg, value)
|
||||
#define DN_Print_ESCColourFgU32(value) DN_Print_ESCColourU32Str8(DN_PrintESCColour_Fg, value).data
|
||||
#define DN_Print_ESCColourBgU32(value) DN_Print_ESCColourU32Str8(DN_PrintESCColour_Bg, value).data
|
||||
|
||||
#define DN_Print_ESCReset "\x1b[0m"
|
||||
#define DN_Print_ESCBold "\x1b[1m"
|
||||
#define DN_Print_ESCResetStr8 DN_STR8(DN_Print_ESCReset)
|
||||
#define DN_Print_ESCBoldStr8 DN_STR8(DN_Print_ESCBold)
|
||||
// NOTE: [$LLOG] DN_Log ///////////////////////////////////////////////////////////////////////////
|
||||
#define DN_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
|
||||
#define DN_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
|
||||
#define DN_LogTypeColourU32_Error 0xff'00'00'ff // Red
|
||||
|
||||
#define DN_Log_DebugF(fmt, ...) DN_Log_TypeFCallSite (DN_LogType_Debug, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define DN_Log_InfoF(fmt, ...) DN_Log_TypeFCallSite (DN_LogType_Info, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define DN_Log_WarningF(fmt, ...) DN_Log_TypeFCallSite (DN_LogType_Warning, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define DN_Log_ErrorF(fmt, ...) DN_Log_TypeFCallSite (DN_LogType_Error, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define DN_Log_DebugFV(fmt, args) DN_Log_TypeFVCallSite(DN_LogType_Debug, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_InfoFV(fmt, args) DN_Log_TypeFVCallSite(DN_LogType_Info, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_WarningFV(fmt, args) DN_Log_TypeFVCallSite(DN_LogType_Warning, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_ErrorFV(fmt, args) DN_Log_TypeFVCallSite(DN_LogType_Error, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_TypeFV(type, fmt, args) DN_Log_TypeFVCallSite(type, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_TypeF(type, fmt, ...) DN_Log_TypeFCallSite (type, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define DN_Log_FV(type, fmt, args) DN_Log_FVCallSite (type, DN_CALL_SITE, fmt, args)
|
||||
#define DN_Log_F(type, fmt, ...) DN_Log_FCallSite (type, DN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
DN_API DN_Str8 DN_Log_MakeStr8 (struct DN_Arena *arena, bool colour, DN_Str8 type, int log_type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_Log_TypeFVCallSite (DN_LogType type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
DN_API void DN_Log_TypeFCallSite (DN_LogType type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Log_FVCallSite (DN_Str8 type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
DN_API void DN_Log_FCallSite (DN_Str8 type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
// NOTE: [$ERRS] DN_ErrSink /////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_ErrSink * DN_ErrSink_Begin (DN_ErrSinkMode mode);
|
||||
#define DN_ErrSink_BeginDefault() DN_ErrSink_Begin(DN_ErrSinkMode_Nil)
|
||||
DN_API bool DN_ErrSink_HasError (DN_ErrSink *err);
|
||||
DN_API DN_ErrSinkMsg* DN_ErrSink_End (DN_Arena *arena, DN_ErrSink *err);
|
||||
DN_API DN_Str8 DN_ErrSink_EndStr8 (DN_Arena *arena, DN_ErrSink *err);
|
||||
DN_API void DN_ErrSink_EndAndIgnore (DN_ErrSink *err);
|
||||
|
||||
#define DN_ErrSink_EndAndLogError(err, err_msg) DN_ErrSink_EndAndLogError_ (err, DN_CALL_SITE, err_msg)
|
||||
#define DN_ErrSink_EndAndLogErrorFV(err, fmt, args) DN_ErrSink_EndAndLogErrorFV_ (err, DN_CALL_SITE, fmt, args)
|
||||
#define DN_ErrSink_EndAndLogErrorF(err, fmt, ...) DN_ErrSink_EndAndLogErrorF_ (err, DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_ErrSink_EndAndExitIfErrorFV(err, exit_val, fmt, args) DN_ErrSink_EndAndExitIfErrorFV_(err, DN_CALL_SITE, exit_val, fmt, args)
|
||||
#define DN_ErrSink_EndAndExitIfErrorF(err, exit_val, fmt, ...) DN_ErrSink_EndAndExitIfErrorF_ (err, DN_CALL_SITE, exit_val, fmt, ##__VA_ARGS__)
|
||||
|
||||
DN_API bool DN_ErrSink_EndAndLogError_ (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg);
|
||||
DN_API bool DN_ErrSink_EndAndLogErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_ErrSink_EndAndLogErrorF_ (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_ErrSink_EndAndExitIfErrorF_ (DN_ErrSink *err, DN_CallSite call_site, uint32_t exit_val, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_ErrSink_EndAndExitIfErrorFV_ (DN_ErrSink *err, DN_CallSite call_site, uint32_t exit_val, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
#define DN_ErrSink_AppendFV(error, error_code, fmt, args) do { DN_TLS_SaveCallSite; DN_ErrSink_AppendFV_(error, error_code, fmt, args); } while (0)
|
||||
#define DN_ErrSink_AppendF(error, error_code, fmt, ...) do { DN_TLS_SaveCallSite; DN_ErrSink_AppendF_(error, error_code, fmt, ## __VA_ARGS__); } while (0)
|
||||
DN_API void DN_ErrSink_AppendFV_ (DN_ErrSink *err, uint32_t error_code, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_ErrSink_AppendF_ (DN_ErrSink *err, uint32_t error_code, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
// NOTE: [$SDLL] DN_SentinelDLL ///////////////////////////////////////////////////////////////////
|
||||
#define DN_SentinelDLL_Init(list) \
|
||||
(list)->next = (list)->prev = (list)
|
||||
|
||||
#define DN_SentinelDLL_InitArena(list, T, arena) \
|
||||
do { \
|
||||
(list) = DN_Arena_New(arena, T, DN_ZeroMem_Yes); \
|
||||
DN_SentinelDLL_Init(list); \
|
||||
} while (0)
|
||||
|
||||
#define DN_SentinelDLL_InitPool(list, T, pool) \
|
||||
do { \
|
||||
(list) = DN_Pool_New(pool, T); \
|
||||
DN_SentinelDLL_Init(list); \
|
||||
} while (0)
|
||||
|
||||
#define DN_SentinelDLL_Detach(item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
(item)->prev->next = (item)->next; \
|
||||
(item)->next->prev = (item)->prev; \
|
||||
(item)->next = nullptr; \
|
||||
(item)->prev = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_SentinelDLL_Dequeue(list, dest_ptr) \
|
||||
if (DN_SentinelDLL_HasItems(list)) { \
|
||||
dest_ptr = (list)->next; \
|
||||
DN_SentinelDLL_Detach(dest_ptr); \
|
||||
}
|
||||
|
||||
#define DN_SentinelDLL_Append(list, item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
if ((item)->next) \
|
||||
DN_SentinelDLL_Detach(item); \
|
||||
(item)->next = (list)->next; \
|
||||
(item)->prev = (list); \
|
||||
(item)->next->prev = (item); \
|
||||
(item)->prev->next = (item); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_SentinelDLL_Prepend(list, item) \
|
||||
do { \
|
||||
if (item) { \
|
||||
if ((item)->next) \
|
||||
DN_SentinelDLL_Detach(item); \
|
||||
(item)->next = (list); \
|
||||
(item)->prev = (list)->prev; \
|
||||
(item)->next->prev = (item); \
|
||||
(item)->prev->next = (item); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_SentinelDLL_IsEmpty(list) \
|
||||
(!(list) || ((list) == (list)->next))
|
||||
|
||||
#define DN_SentinelDLL_IsInit(list) \
|
||||
((list)->next && (list)->prev)
|
||||
|
||||
#define DN_SentinelDLL_HasItems(list) \
|
||||
((list) && ((list) != (list)->next))
|
||||
|
||||
#define DN_SentinelDLL_ForEach(it_name, list) \
|
||||
auto *it_name = (list)->next; (it_name) != (list); (it_name) = (it_name)->next
|
||||
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
DN_FORCE_INLINE uint64_t DN_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||
{
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
__int64 result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (DN_Atomic_CompareExchange64(target, value, result) != result);
|
||||
return DN_CAST(uint64_t)result;
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
uint64_t result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_FORCE_INLINE long DN_Atomic_SetValue32(long volatile *target, long value)
|
||||
{
|
||||
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
||||
long result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (DN_Atomic_CompareExchange32(target, value, result) != result);
|
||||
return result;
|
||||
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
|
||||
long result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
template <DN_USize N> bool DN_CPU_HasAllFeaturesCArray(DN_CPUReport const *report, DN_CPUFeature const (&features)[N])
|
||||
{
|
||||
bool result = DN_CPU_HasAllFeatures(report, features, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern struct DN_Core *g_dn_core;
|
1238
dqn_cgen.cpp
1238
dqn_cgen.cpp
File diff suppressed because it is too large
Load Diff
174
dqn_cgen.h
174
dqn_cgen.h
@ -1,174 +0,0 @@
|
||||
#if !defined(DN_CGEN_H)
|
||||
#define DN_CGEN_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$ _____|$$$\ $$ |
|
||||
// $$ / \__|$$ / \__|$$ | $$$$\ $$ |
|
||||
// $$ | $$ |$$$$\ $$$$$\ $$ $$\$$ |
|
||||
// $$ | $$ |\_$$ |$$ __| $$ \$$$$ |
|
||||
// $$ | $$\ $$ | $$ |$$ | $$ |\$$$ |
|
||||
// \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | \$$ |
|
||||
// \______/ \______/ \________|\__| \__|
|
||||
//
|
||||
// dqn_cgen.h -- C/C++ code generation from table data in Metadesk files
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$CGEN] DN_CGen //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(MD_H)
|
||||
#error Metadesk 'md.h' must be included before 'dn_cgen.h'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_H)
|
||||
#error 'dqn.h' must be included before 'dn_cgen.h'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_CPP_FILE_H)
|
||||
#error 'dn_cpp_file.h' must be included before 'dn_cgen.h'
|
||||
#endif
|
||||
|
||||
enum DN_CGenTableKeyType
|
||||
{
|
||||
DN_CGenTableKeyType_Nil,
|
||||
DN_CGenTableKeyType_Name,
|
||||
DN_CGenTableKeyType_Type,
|
||||
};
|
||||
|
||||
enum DN_CGenTableType
|
||||
{
|
||||
DN_CGenTableType_Nil,
|
||||
DN_CGenTableType_Data,
|
||||
DN_CGenTableType_CodeGenBuiltinTypes,
|
||||
DN_CGenTableType_CodeGenStruct,
|
||||
DN_CGenTableType_CodeGenEnum,
|
||||
DN_CGenTableType_Count,
|
||||
};
|
||||
|
||||
enum DN_CGenTableRowTagType
|
||||
{
|
||||
DN_CGenTableRowTagType_Nil,
|
||||
DN_CGenTableRowTagType_CommentDivider,
|
||||
DN_CGenTableRowTagType_EmptyLine,
|
||||
};
|
||||
|
||||
enum DN_CGenTableRowTagCommentDivider
|
||||
{
|
||||
DN_CGenTableRowTagCommentDivider_Nil,
|
||||
DN_CGenTableRowTagCommentDivider_Label,
|
||||
};
|
||||
|
||||
enum DN_CGenTableHeaderType
|
||||
{
|
||||
DN_CGenTableHeaderType_Name,
|
||||
DN_CGenTableHeaderType_Table,
|
||||
DN_CGenTableHeaderType_CppType,
|
||||
DN_CGenTableHeaderType_CppName,
|
||||
DN_CGenTableHeaderType_CppValue,
|
||||
DN_CGenTableHeaderType_CppIsPtr,
|
||||
DN_CGenTableHeaderType_CppOpEquals,
|
||||
DN_CGenTableHeaderType_CppArraySize,
|
||||
DN_CGenTableHeaderType_CppArraySizeField,
|
||||
DN_CGenTableHeaderType_CppLabel,
|
||||
DN_CGenTableHeaderType_GenTypeInfo,
|
||||
DN_CGenTableHeaderType_GenEnumCount,
|
||||
DN_CGenTableHeaderType_Count,
|
||||
};
|
||||
|
||||
struct DN_CGenTableHeader
|
||||
{
|
||||
MD_String8 name;
|
||||
int longest_string;
|
||||
};
|
||||
|
||||
struct DN_CGenTableRowTag
|
||||
{
|
||||
DN_CGenTableRowTagType type;
|
||||
MD_String8 comment;
|
||||
DN_CGenTableRowTag *next;
|
||||
};
|
||||
|
||||
|
||||
struct DN_CGenTableColumn
|
||||
{
|
||||
MD_Node *node;
|
||||
DN_Str8 string;
|
||||
};
|
||||
|
||||
struct DN_CGenTableRow
|
||||
{
|
||||
DN_CGenTableRowTag *first_tag;
|
||||
DN_CGenTableRowTag *last_tag;
|
||||
DN_CGenTableColumn *columns;
|
||||
};
|
||||
|
||||
struct DN_CGenTable
|
||||
{
|
||||
DN_CGenTableType type;
|
||||
DN_Str8 name;
|
||||
MD_Map headers_map;
|
||||
DN_CGenTableHeader *headers;
|
||||
DN_CGenTableRow *rows;
|
||||
size_t column_count;
|
||||
size_t row_count;
|
||||
|
||||
MD_Node *node;
|
||||
MD_Node *headers_node;
|
||||
DN_USize column_indexes[DN_CGenTableHeaderType_Count];
|
||||
DN_CGenTable *next;
|
||||
};
|
||||
|
||||
struct DN_CGen
|
||||
{
|
||||
MD_Arena *arena;
|
||||
MD_Node *file_list;
|
||||
MD_Map table_map;
|
||||
DN_CGenTable *first_table;
|
||||
DN_CGenTable *last_table;
|
||||
DN_USize table_counts[DN_CGenTableType_Count];
|
||||
};
|
||||
|
||||
struct DN_CGenMapNodeToEnum
|
||||
{
|
||||
uint32_t enum_val;
|
||||
DN_Str8 node_string;
|
||||
};
|
||||
|
||||
struct DN_CGenLookupTableIterator
|
||||
{
|
||||
DN_CGenTable *cgen_table;
|
||||
DN_CGenTableRow *cgen_table_row;
|
||||
DN_CGenTableColumn cgen_table_column[DN_CGenTableHeaderType_Count];
|
||||
DN_CGenTable *table;
|
||||
DN_USize row_index;
|
||||
};
|
||||
|
||||
struct DN_CGenLookupColumnAtHeader
|
||||
{
|
||||
DN_USize index;
|
||||
DN_CGenTableHeader header;
|
||||
DN_CGenTableColumn column;
|
||||
};
|
||||
|
||||
enum DN_CGenEmit
|
||||
{
|
||||
DN_CGenEmit_Prototypes = 1 << 0,
|
||||
DN_CGenEmit_Implementation = 1 << 1,
|
||||
};
|
||||
|
||||
// NOTE: [$CGEN] DN_CGen //////////////////////////////////////////////////////////////////////////
|
||||
#define DN_CGen_MDToDNStr8(str8) DN_Str8_Init((str8).str, (str8).size)
|
||||
#define DN_CGen_DNToMDStr8(str8) {DN_CAST(MD_u8 *)(str8).data, (str8).size}
|
||||
DN_API DN_CGen DN_CGen_InitFilesArgV (int argc, char const **argv, DN_ErrSink *err);
|
||||
DN_API DN_Str8 DN_CGen_TableHeaderTypeToDeclStr8 (DN_CGenTableHeaderType type);
|
||||
DN_API DN_CGenMapNodeToEnum DN_CGen_MapNodeToEnumOrExit (MD_Node const *node, DN_CGenMapNodeToEnum const *valid_keys, DN_USize valid_keys_size, char const *fmt, ...);
|
||||
DN_API DN_USize DN_CGen_NodeChildrenCount (MD_Node const *node);
|
||||
DN_API void DN_CGen_LogF (MD_MessageKind kind, MD_Node *node, DN_ErrSink *err, char const *fmt, ...);
|
||||
DN_API bool DN_CGen_TableHasHeaders (DN_CGenTable const *table, DN_Str8 const *headers, DN_USize header_count, DN_ErrSink *err);
|
||||
DN_API DN_CGenLookupColumnAtHeader DN_CGen_LookupColumnAtHeader (DN_CGenTable *table, DN_Str8 header, DN_CGenTableRow const *row);
|
||||
DN_API bool DN_CGen_LookupNextTableInCodeGenTable(DN_CGen *cgen, DN_CGenTable *cgen_table, DN_CGenLookupTableIterator *it);
|
||||
DN_API void DN_CGen_EmitCodeForTables (DN_CGen *cgen, DN_CGenEmit emit, DN_CppFile *cpp, DN_Str8 emit_prefix);
|
||||
#endif // DN_CGEN_H
|
1406
dqn_containers.cpp
1406
dqn_containers.cpp
File diff suppressed because it is too large
Load Diff
347
dqn_containers.h
347
dqn_containers.h
@ -1,347 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ / $$ | $$ | $$$$\ $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$$$$ | $$ | $$ $$\$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __$$ | $$ | $$ \$$$$ |$$ __| $$ __$$< \____$$\
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |$$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \______/ \______/ \__| \__| \__| \__| \__|\______|\__| \__|\________|\__| \__| \______/
|
||||
//
|
||||
// dqn_containers.h -- Data structures for storing data (e.g. arrays and hash tables)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$CARR] DN_CArray -- -- Basic operations on C arrays for VArray/SArray/FArray to reuse
|
||||
// [$VARR] DN_VArray -- DN_VARRAY -- Array backed by virtual memory arena
|
||||
// [$SARR] DN_SArray -- DN_SARRAY -- Array that are allocated but cannot resize
|
||||
// [$FARR] DN_FArray -- DN_FARRAY -- Fixed-size arrays
|
||||
// [$SLIC] DN_Slice -- -- Pointe and length container of data
|
||||
// [$DMAP] DN_DSMap -- DN_DSMAP -- Hashtable, 70% max load, PoT size, linear probe, chain repair
|
||||
// [$LIST] DN_List -- DN_LIST -- Chunked linked lists, append only
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$CARR] DN_CArray ////////////////////////////////////////////////////////////////////////
|
||||
enum DN_ArrayErase
|
||||
{
|
||||
DN_ArrayErase_Unstable,
|
||||
DN_ArrayErase_Stable,
|
||||
};
|
||||
|
||||
struct DN_ArrayEraseResult
|
||||
{
|
||||
// The next index your for-index should be set to such that you can continue
|
||||
// to iterate the remainder of the array, e.g:
|
||||
//
|
||||
// for (DN_USize index = 0; index < array.size; index++) {
|
||||
// if (erase)
|
||||
// index = DN_FArray_EraseRange(&array, index, -3, DN_ArrayErase_Unstable);
|
||||
// }
|
||||
DN_USize it_index;
|
||||
DN_USize items_erased; // The number of items erased
|
||||
};
|
||||
|
||||
template <typename T> struct DN_ArrayFindResult
|
||||
{
|
||||
T *data; // Pointer to the value if a match is found, null pointer otherwise
|
||||
DN_USize index; // Index to the value if a match is found, 0 otherwise
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_VARRAY)
|
||||
// NOTE: [$VARR] DN_VArray ////////////////////////////////////////////////////////////////////////
|
||||
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to
|
||||
// the OS.
|
||||
template <typename T> struct DN_VArray
|
||||
{
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
DN_USize max; // Maximum number of items this array can store
|
||||
DN_USize commit; // Bytes committed
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
#endif // !defined(DN_NO_VARRAY)
|
||||
|
||||
#if !defined(DN_NO_SARRAY)
|
||||
// NOTE: [$SARR] DN_SArray ////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> struct DN_SArray
|
||||
{
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
DN_USize max; // Maximum number of items this array can store
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
#endif // !defined(DN_NO_SARRAY)
|
||||
|
||||
#if !defined(DN_NO_FARRAY)
|
||||
// NOTE: [$FARR] DN_FArray ////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, DN_USize N> struct DN_FArray
|
||||
{
|
||||
T data[N]; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
template <typename T> using DN_FArray8 = DN_FArray<T, 8>;
|
||||
template <typename T> using DN_FArray16 = DN_FArray<T, 16>;
|
||||
template <typename T> using DN_FArray32 = DN_FArray<T, 32>;
|
||||
template <typename T> using DN_FArray64 = DN_FArray<T, 64>;
|
||||
#endif // !defined(DN_NO_FARRAY)
|
||||
|
||||
#if !defined(DN_NO_DSMAP)
|
||||
// NOTE: [$DMAP] DN_DSMap /////////////////////////////////////////////////////////////////////////
|
||||
enum DN_DSMapKeyType
|
||||
{
|
||||
// Key | Key Hash | Map Index
|
||||
DN_DSMapKeyType_Invalid,
|
||||
DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size
|
||||
DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size
|
||||
DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size
|
||||
DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size
|
||||
};
|
||||
|
||||
struct DN_DSMapKey
|
||||
{
|
||||
DN_DSMapKeyType type;
|
||||
DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches
|
||||
void const *buffer_data;
|
||||
DN_U32 buffer_size;
|
||||
DN_U64 u64;
|
||||
bool no_copy_buffer;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DN_DSMapSlot
|
||||
{
|
||||
DN_DSMapKey key; // Hash table lookup key
|
||||
T value; // Hash table value
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_DSMapFlags;
|
||||
enum DN_DSMapFlags_
|
||||
{
|
||||
DN_DSMapFlags_Nil = 0,
|
||||
DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0,
|
||||
};
|
||||
|
||||
using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed);
|
||||
template <typename T> struct DN_DSMap
|
||||
{
|
||||
DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array
|
||||
DN_DSMapSlot<T> *slots; // Values of the array stored contiguously, non-sorted order
|
||||
DN_U32 size; // Total capacity of the map and is a power of two
|
||||
DN_U32 occupied; // Number of slots used in the hash table
|
||||
DN_Arena *arena; // Backing arena for the hash table
|
||||
DN_Pool pool; // Allocator for keys that are variable-sized buffers
|
||||
DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size
|
||||
DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set
|
||||
DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used
|
||||
DN_DSMapFlags flags;
|
||||
};
|
||||
|
||||
template <typename T> struct DN_DSMapResult
|
||||
{
|
||||
bool found;
|
||||
DN_DSMapSlot<T> *slot;
|
||||
T *value;
|
||||
};
|
||||
#endif // !defined(DN_NO_DSMAP)
|
||||
|
||||
#if !defined(DN_NO_LIST)
|
||||
// NOTE: [$LIST] DN_List //////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> struct DN_ListChunk
|
||||
{
|
||||
T *data;
|
||||
DN_USize size;
|
||||
DN_USize count;
|
||||
DN_ListChunk<T> *next;
|
||||
DN_ListChunk<T> *prev;
|
||||
};
|
||||
|
||||
template <typename T> struct DN_ListIterator
|
||||
{
|
||||
DN_B32 init; // True if DN_List_Iterate has been called at-least once on this iterator
|
||||
DN_ListChunk<T> *chunk; // The chunk that the iterator is reading from
|
||||
DN_USize chunk_data_index; // The index in the chunk the iterator is referencing
|
||||
DN_USize index; // The index of the item in the list as if it was flat array
|
||||
T *data; // Pointer to the data the iterator is referencing. Nullptr if invalid.
|
||||
};
|
||||
|
||||
template <typename T> struct DN_List
|
||||
{
|
||||
DN_USize count; // Cumulative count of all items made across all list chunks
|
||||
DN_USize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node.
|
||||
DN_ListChunk<T> *head;
|
||||
DN_ListChunk<T> *tail;
|
||||
};
|
||||
#endif // !defined(DN_NO_LIST)
|
||||
|
||||
template <typename T> DN_ArrayEraseResult DN_CArray_EraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> T * DN_CArray_MakeArray (T *data, DN_USize *size, DN_USize max, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_CArray_InsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T> T DN_CArray_PopFront (T *data, DN_USize *size, DN_USize count);
|
||||
template <typename T> T DN_CArray_PopBack (T *data, DN_USize *size, DN_USize count);
|
||||
template <typename T> DN_ArrayFindResult<T> DN_CArray_Find (T *data, DN_USize size, T const &value);
|
||||
|
||||
#if !defined(DN_NO_VARRAY)
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitByteSize (DN_USize byte_size);
|
||||
template <typename T> DN_VArray<T> DN_VArray_Init (DN_USize max);
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitSlice (DN_Slice<T> slice, DN_USize max);
|
||||
template <typename T, DN_USize N> DN_VArray<T> DN_VArray_InitCArray (T const (&items)[N], DN_USize max);
|
||||
template <typename T> void DN_VArray_Deinit (DN_VArray<T> *array);
|
||||
template <typename T> bool DN_VArray_IsValid (DN_VArray<T> const *array);
|
||||
template <typename T> DN_Slice<T> DN_VArray_Slice (DN_VArray<T> const *array);
|
||||
template <typename T> bool DN_VArray_Reserve (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T * DN_VArray_AddArray (DN_VArray<T> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_AddCArray (DN_VArray<T> *array, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Add (DN_VArray<T> *array, T const &item);
|
||||
#define DN_VArray_AddArrayAssert(...) DN_HARD_ASSERT(DN_VArray_AddArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddCArrayAssert(...) DN_HARD_ASSERT(DN_VArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddAssert(...) DN_HARD_ASSERT(DN_VArray_Add(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_MakeArray (DN_VArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_VArray_Make (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_VArray_MakeArrayAssert(...) DN_HARD_ASSERT(DN_VArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_VArray_MakeAssert(...) DN_HARD_ASSERT(DN_VArray_Make(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_InsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_InsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Insert (DN_VArray<T> *array, DN_USize index, T const &item);
|
||||
#define DN_VArray_InsertArrayAssert(...) DN_HARD_ASSERT(DN_VArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertCArrayAssert(...) DN_HARD_ASSERT(DN_VArray_InsertCArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertAssert(...) DN_HARD_ASSERT(DN_VArray_Insert(__VA_ARGS__))
|
||||
template <typename T> T DN_VArray_PopFront (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T DN_VArray_PopBack (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> DN_ArrayEraseResult DN_VArray_EraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> void DN_VArray_Clear (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#endif // !defined(DN_NO_VARRAY)
|
||||
// NOTE: [$SARR] DN_SArray ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_SARRAY)
|
||||
template <typename T> DN_SArray<T> DN_SArray_Init (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
|
||||
template <typename T> DN_SArray<T> DN_SArray_InitSlice (DN_Arena *arena, DN_Slice<T> slice, DN_USize size, DN_ZeroMem zero_mem);
|
||||
template <typename T, size_t N> DN_SArray<T> DN_SArray_InitCArray (DN_Arena *arena, T const (&array)[N], DN_USize size, DN_ZeroMem);
|
||||
template <typename T> DN_SArray<T> DN_SArray_InitBuffer (T* buffer, DN_USize size);
|
||||
template <typename T> bool DN_SArray_IsValid (DN_SArray<T> const *array);
|
||||
template <typename T> DN_Slice<T> DN_SArray_Slice (DN_SArray<T> const *array);
|
||||
template <typename T> T * DN_SArray_AddArray (DN_SArray<T> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_SArray_AddCArray (DN_SArray<T> *array, T const (&items)[N]);
|
||||
template <typename T> T * DN_SArray_Add (DN_SArray<T> *array, T const &item);
|
||||
#define DN_SArray_AddArrayAssert(...) DN_HARD_ASSERT(DN_SArray_AddArray(__VA_ARGS__))
|
||||
#define DN_SArray_AddCArrayAssert(...) DN_HARD_ASSERT(DN_SArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_SArray_AddAssert(...) DN_HARD_ASSERT(DN_SArray_Add(__VA_ARGS__))
|
||||
template <typename T> T * DN_SArray_MakeArray (DN_SArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_SArray_Make (DN_SArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_SArray_MakeArrayAssert(...) DN_HARD_ASSERT(DN_SArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_SArray_MakeAssert(...) DN_HARD_ASSERT(DN_SArray_Make(__VA_ARGS__))
|
||||
template <typename T> T * DN_SArray_InsertArray (DN_SArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_SArray_InsertCArray (DN_SArray<T> *array, DN_USize index, T const (&items)[N]);
|
||||
template <typename T> T * DN_SArray_Insert (DN_SArray<T> *array, DN_USize index, T const &item);
|
||||
#define DN_SArray_InsertArrayAssert(...) DN_HARD_ASSERT(DN_SArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_SArray_InsertCArrayAssert(...) DN_HARD_ASSERT(DN_SArray_InsertCArray(__VA_ARGS__))
|
||||
#define DN_SArray_InsertAssert(...) DN_HARD_ASSERT(DN_SArray_Insert(__VA_ARGS__))
|
||||
template <typename T> T DN_SArray_PopFront (DN_SArray<T> *array, DN_USize count);
|
||||
template <typename T> T DN_SArray_PopBack (DN_SArray<T> *array, DN_USize count);
|
||||
template <typename T> DN_ArrayEraseResult DN_SArray_EraseRange (DN_SArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> void DN_SArray_Clear (DN_SArray<T> *array);
|
||||
#endif // !defined(DN_NO_SARRAY)
|
||||
#if !defined(DN_NO_FARRAY)
|
||||
#define DN_FArray_ToSArray(array) DN_SArray_InitBuffer((array)->data, DN_ARRAY_UCOUNT((array)->data))
|
||||
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_Init (T const *array, DN_USize count);
|
||||
#define DN_FArray_HasData(array) ((array).data && (array).size)
|
||||
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_InitSlice (DN_Slice<T> slice);
|
||||
template <typename T, DN_USize N, DN_USize K> DN_FArray<T, N> DN_FArray_InitCArray (T const (&items)[K]);
|
||||
template <typename T, DN_USize N> bool DN_FArray_IsValid (DN_FArray<T, N> const *array);
|
||||
template <typename T, DN_USize N> DN_USize DN_FArray_Max (DN_FArray<T, N> const *) { return N; }
|
||||
template <typename T, DN_USize N> DN_Slice<T> DN_FArray_Slice (DN_FArray<T, N> const *array);
|
||||
template <typename T, DN_USize N> T * DN_FArray_AddArray (DN_FArray<T, N> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_AddCArray (DN_FArray<T, N> *array, T const (&items)[K]);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Add (DN_FArray<T, N> *array, T const &item);
|
||||
#define DN_FArray_AddArrayAssert(...) DN_HARD_ASSERT(DN_FArray_AddArray(__VA_ARGS__))
|
||||
#define DN_FArray_AddCArrayAssert(...) DN_HARD_ASSERT(DN_FArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_FArray_AddAssert(...) DN_HARD_ASSERT(DN_FArray_Add(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T * DN_FArray_MakeArray (DN_FArray<T, N> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Make (DN_FArray<T, N> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_FArray_MakeArrayAssert(...) DN_HARD_ASSERT(DN_FArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_FArray_MakeAssert(...) DN_HARD_ASSERT(DN_FArray_Make(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T * DN_FArray_InsertArray (DN_FArray<T, N> *array, T const &item, DN_USize index);
|
||||
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_InsertCArray (DN_FArray<T, N> *array, DN_USize index, T const (&items)[K]);
|
||||
template <typename T, DN_USize N> T * DN_FArray_Insert (DN_FArray<T, N> *array, DN_USize index, T const &item);
|
||||
#define DN_FArray_InsertArrayAssert(...) DN_HARD_ASSERT(DN_FArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_FArray_InsertAssert(...) DN_HARD_ASSERT(DN_FArray_Insert(__VA_ARGS__))
|
||||
template <typename T, DN_USize N> T DN_FArray_PopFront (DN_FArray<T, N> *array, DN_USize count);
|
||||
template <typename T, DN_USize N> T DN_FArray_PopBack (DN_FArray<T, N> *array, DN_USize count);
|
||||
template <typename T, DN_USize N> DN_ArrayFindResult<T> DN_FArray_Find (DN_FArray<T, N> *array, T const &find);
|
||||
template <typename T, DN_USize N> DN_ArrayEraseResult DN_FArray_EraseRange (DN_FArray<T, N> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T, DN_USize N> void DN_FArray_Clear (DN_FArray<T, N> *array);
|
||||
#endif // !defined(DN_NO_FARRAY)
|
||||
#if !defined(DN_NO_SLICE)
|
||||
#define DN_TO_SLICE(val) DN_Slice_Init((val)->data, (val)->size)
|
||||
template <typename T> DN_Slice<T> DN_Slice_Init (T* const data, DN_USize size);
|
||||
template <typename T, DN_USize N> DN_Slice<T> DN_Slice_InitCArray (DN_Arena *arena, T const (&array)[N]);
|
||||
template <typename T> DN_Slice<T> DN_Slice_Copy (DN_Arena *arena, DN_Slice<T> slice);
|
||||
template <typename T> DN_Slice<T> DN_Slice_CopyPtr (DN_Arena *arena, T* const data, DN_USize size);
|
||||
template <typename T> DN_Slice<T> DN_Slice_Alloc (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_Str8 DN_Slice_Str8Render (DN_Arena *arena, DN_Slice<DN_Str8> array, DN_Str8 separator);
|
||||
DN_Str8 DN_Slice_Str8RenderSpaceSeparated (DN_Arena *arena, DN_Slice<DN_Str8> array);
|
||||
DN_Str16 DN_Slice_Str16Render (DN_Arena *arena, DN_Slice<DN_Str16> array, DN_Str16 separator);
|
||||
DN_Str16 DN_Slice_Str16RenderSpaceSeparated(DN_Arena *arena, DN_Slice<DN_Str16> array);
|
||||
#endif // !defined(DN_NO_SLICE)
|
||||
#if !defined(DN_NO_DSMAP)
|
||||
template <typename T> DN_DSMap<T> DN_DSMap_Init (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
||||
template <typename T> void DN_DSMap_Deinit (DN_DSMap<T> *map, DN_ZeroMem zero_mem);
|
||||
template <typename T> bool DN_DSMap_IsValid (DN_DSMap<T> const *map);
|
||||
template <typename T> DN_U32 DN_DSMap_Hash (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_U32 DN_DSMap_HashToSlotIndex (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Find (DN_DSMap<T> const *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Make (DN_DSMap<T> *map, DN_DSMapKey key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_Set (DN_DSMap<T> *map, DN_DSMapKey key, T const &value);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyU64 (DN_DSMap<T> const *map, DN_U64 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyU64 (DN_DSMap<T> *map, DN_U64 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyU64 (DN_DSMap<T> *map, DN_U64 key, T const &value);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyStr8 (DN_DSMap<T> const *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyStr8 (DN_DSMap<T> *map, DN_Str8 key, T const &value);
|
||||
template <typename T> bool DN_DSMap_Resize (DN_DSMap<T> *map, DN_U32 size);
|
||||
template <typename T> bool DN_DSMap_Erase (DN_DSMap<T> *map, DN_DSMapKey key);
|
||||
template <typename T> bool DN_DSMap_EraseKeyU64 (DN_DSMap<T> *map, DN_U64 key);
|
||||
template <typename T> bool DN_DSMap_EraseKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyBuffer (DN_DSMap<T> const *map, void const *data, DN_U32 size);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyBufferAsU64NoHash (DN_DSMap<T> const *map, void const *data, DN_U32 size);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyU64 (DN_DSMap<T> const *map, DN_U64 u64);
|
||||
template <typename T> DN_DSMapKey DN_DSMap_KeyStr8 (DN_DSMap<T> const *map, DN_Str8 string);
|
||||
#define DN_DSMap_KeyCStr8(map, string) DN_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1)
|
||||
DN_API DN_DSMapKey DN_DSMap_KeyU64NoHash (DN_U64 u64);
|
||||
DN_API bool DN_DSMap_KeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||
DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||
#endif // !defined(DN_NO_DSMAP)
|
||||
#if !defined(DN_NO_LIST)
|
||||
template <typename T> DN_List<T> DN_List_Init (DN_USize chunk_size);
|
||||
template <typename T, size_t N> DN_List<T> DN_List_InitCArray (DN_Arena *arena, DN_USize chunk_size, T const (&array)[N]);
|
||||
template <typename T> T * DN_List_At (DN_List<T> *list, DN_USize index, DN_ListChunk<T> **at_chunk);
|
||||
template <typename T> void DN_List_Clear (DN_List<T> *list);
|
||||
template <typename T> bool DN_List_Iterate (DN_List<T> *list, DN_ListIterator<T> *it, DN_USize start_index);
|
||||
template <typename T> T * DN_List_MakeArena (DN_List<T> *list, DN_Arena *arena, DN_USize count);
|
||||
template <typename T> T * DN_List_MakePool (DN_List<T> *list, DN_Pool *pool, DN_USize count);
|
||||
template <typename T> T * DN_List_AddArena (DN_List<T> *list, DN_Arena *arena, T const &value);
|
||||
template <typename T> T * DN_List_AddPool (DN_List<T> *list, DN_Pool *pool, T const &value);
|
||||
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Arena *arena, DN_List<T> other);
|
||||
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Pool *pool, DN_List<T> other);
|
||||
template <typename T> DN_Slice<T> DN_List_ToSliceCopy (DN_List<T> const *list, DN_Arena* arena);
|
||||
#endif // !defined(DN_NO_LIST)
|
||||
|
509
dqn_debug.cpp
509
dqn_debug.cpp
@ -1,509 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ _____|$$ __$$\ $$ | $$ |$$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$\ $$$$$$$\ |$$ | $$ |$$ |$$$$\
|
||||
// $$ | $$ |$$ __| $$ __$$\ $$ | $$ |$$ |\_$$ |
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ | $$ |
|
||||
// $$$$$$$ |$$$$$$$$\ $$$$$$$ |\$$$$$$ |\$$$$$$ |
|
||||
// \_______/ \________|\_______/ \______/ \______/
|
||||
//
|
||||
// dqn_debug.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$ASAN] DN_Asan ////////////////////////////////////////////////////////////////////////// ///
|
||||
DN_API void DN_ASAN_PoisonMemoryRegion(void const volatile *ptr, DN_USize size)
|
||||
{
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
DN_ASSERTF(DN_IsPowerOfTwoAligned(ptr, 8),
|
||||
"Poisoning requires the pointer to be aligned on an 8 byte boundary");
|
||||
|
||||
__asan_poison_memory_region(ptr, size);
|
||||
if (DN_ASAN_VET_POISON) {
|
||||
DN_HARD_ASSERT(__asan_address_is_poisoned(ptr));
|
||||
DN_HARD_ASSERT(__asan_address_is_poisoned((char *)ptr + (size - 1)));
|
||||
}
|
||||
#else
|
||||
(void)ptr; (void)size;
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, DN_USize size)
|
||||
{
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
__asan_unpoison_memory_region(ptr, size);
|
||||
if (DN_ASAN_VET_POISON) {
|
||||
DN_HARD_ASSERT(__asan_region_is_poisoned((void *)ptr, size) == 0);
|
||||
}
|
||||
#else
|
||||
(void)ptr; (void)size;
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk(DN_Arena *arena, uint16_t limit)
|
||||
{
|
||||
DN_StackTraceWalkResult result = {};
|
||||
#if defined(DN_OS_WIN32)
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
static DN_TicketMutex mutex = {};
|
||||
DN_TicketMutex_Begin(&mutex);
|
||||
|
||||
HANDLE thread = GetCurrentThread();
|
||||
result.process = GetCurrentProcess();
|
||||
|
||||
if (!g_dn_core->win32_sym_initialised) {
|
||||
g_dn_core->win32_sym_initialised = true;
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
DN_WinError error = DN_Win_LastError(tmem.arena);
|
||||
DN_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_STR_FMT(error.msg));
|
||||
}
|
||||
}
|
||||
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 frame = {};
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
DN_FArray<uint64_t, 256> raw_frames = {};
|
||||
while (raw_frames.size < limit) {
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
|
||||
result.process,
|
||||
thread,
|
||||
&frame,
|
||||
&context,
|
||||
nullptr /*ReadMemoryRoutine*/,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
nullptr /*TranslateAddress*/)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
|
||||
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
|
||||
DN_FArray_Add(&raw_frames, frame.AddrPC.Offset);
|
||||
}
|
||||
DN_TicketMutex_End(&mutex);
|
||||
|
||||
result.base_addr = DN_Arena_NewArray(arena, uint64_t, raw_frames.size, DN_ZeroMem_No);
|
||||
result.size = DN_CAST(uint16_t)raw_frames.size;
|
||||
DN_MEMCPY(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
|
||||
#else
|
||||
(void)limit; (void)arena;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_StackTraceWalkResult DN_StackTrace_WalkCRT(uint16_t limit)
|
||||
{
|
||||
DN_StackTraceWalkResult result = {};
|
||||
#if defined(DN_OS_WIN32)
|
||||
static DN_TicketMutex mutex = {};
|
||||
DN_TicketMutex_Begin(&mutex);
|
||||
|
||||
HANDLE thread = GetCurrentThread();
|
||||
result.process = GetCurrentProcess();
|
||||
|
||||
if (!g_dn_core->win32_sym_initialised) {
|
||||
g_dn_core->win32_sym_initialised = true;
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
||||
DN_WinError error = DN_Win_LastErrorAlloc();
|
||||
DN_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_STR_FMT(error.msg));
|
||||
DN_OS_MemDealloc(error.msg.data);
|
||||
}
|
||||
}
|
||||
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 frame = {};
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
struct FrameChunk
|
||||
{
|
||||
uint64_t frames[128];
|
||||
FrameChunk *next;
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
DN_USize total_frames = 0;
|
||||
FrameChunk frame_chunk = {};
|
||||
FrameChunk *frame_chunk_it = &frame_chunk;
|
||||
for (; total_frames < limit; total_frames++) {
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
|
||||
result.process,
|
||||
thread,
|
||||
&frame,
|
||||
&context,
|
||||
nullptr /*ReadMemoryRoutine*/,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
nullptr /*TranslateAddress*/)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
|
||||
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
|
||||
if (frame_chunk_it->size == DN_ARRAY_UCOUNT(frame_chunk_it->frames)) {
|
||||
FrameChunk *next = DN_CAST(FrameChunk *) DN_OS_MemAlloc(sizeof(*next), DN_ZeroMem_No);
|
||||
frame_chunk_it = next;
|
||||
}
|
||||
|
||||
if (!frame_chunk_it)
|
||||
break;
|
||||
frame_chunk_it->frames[frame_chunk_it->size++] = frame.AddrPC.Offset;
|
||||
}
|
||||
DN_TicketMutex_End(&mutex);
|
||||
|
||||
result.base_addr = DN_CAST(uint64_t *)DN_OS_MemAlloc(sizeof(*result.base_addr) * total_frames, DN_ZeroMem_No);
|
||||
for (FrameChunk *it = &frame_chunk; it; ) {
|
||||
FrameChunk *next = it->next;
|
||||
|
||||
// NOTE: Copy
|
||||
DN_MEMCPY(result.base_addr, it->frames, it->size * sizeof(it->frames[0]));
|
||||
result.size += it->size;
|
||||
|
||||
// NOTE: Free
|
||||
if (it != &frame_chunk)
|
||||
DN_OS_MemDealloc(it);
|
||||
it = next;
|
||||
}
|
||||
#else
|
||||
DN_INVALID_CODE_PATH;
|
||||
(void)limit;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
DN_StackTraceWalkResult walk_result = DN_StackTrace_WalkCRT(limit);
|
||||
DN_Str8 result = DN_StackTrace_WalkResultStr8CRT(&walk_result, skip);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_StackTrace_AddWalkToStr8Builder_(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip)
|
||||
{
|
||||
DN_StackTraceRawFrame raw_frame = {};
|
||||
raw_frame.process = walk->process;
|
||||
for (DN_USize index = skip; index < walk->size; index++) {
|
||||
raw_frame.base_addr = walk->base_addr[index];
|
||||
DN_StackTraceFrame frame = DN_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
|
||||
DN_Str8Builder_AppendF(builder, "%.*s(%zu): %.*s%s", DN_STR_FMT(frame.file_name), frame.line_number, DN_STR_FMT(frame.function_name), (DN_CAST(int)index == walk->size - 1) ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8CRTNoScratch(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
DN_Arena arena = {};
|
||||
arena.flags |= DN_ArenaFlags_NoAllocTrack;
|
||||
DN_DEFER { DN_Arena_Deinit(&arena); };
|
||||
|
||||
DN_Str8Builder builder = {};
|
||||
builder.arena = &arena;
|
||||
|
||||
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(&arena, limit);
|
||||
DN_StackTraceRawFrame raw_frame = {};
|
||||
raw_frame.process = walk.process;
|
||||
for (DN_USize index = skip; index < walk.size; index++) {
|
||||
raw_frame.base_addr = walk.base_addr[index];
|
||||
DN_StackTraceFrame frame = DN_StackTrace_RawFrameToFrame(builder.arena, raw_frame);
|
||||
DN_Str8Builder_AppendF(&builder, "%.*s(%zu): %.*s%s", DN_STR_FMT(frame.file_name), frame.line_number, DN_STR_FMT(frame.function_name), (DN_CAST(int)index == walk.size - 1) ? "" : "\n");
|
||||
}
|
||||
|
||||
DN_Str8 result = {};
|
||||
result.data = DN_CAST(char *)DN_OS_MemReserve(builder.string_size + 1, DN_OSMemCommit_Yes, DN_OSMemPage_ReadWrite);
|
||||
if (result.data) {
|
||||
for (DN_Str8Link *it = builder.head; it; it = it->next) {
|
||||
DN_MEMCPY(result.data + result.size, it->string.data, it->string.size);
|
||||
result.size += it->string.size;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk)
|
||||
{
|
||||
bool result = false;
|
||||
if (!it || !walk || !walk->base_addr || !walk->process)
|
||||
return result;
|
||||
|
||||
if (it->index >= walk->size)
|
||||
return false;
|
||||
|
||||
result = true;
|
||||
it->raw_frame.process = walk->process;
|
||||
it->raw_frame.base_addr = walk->base_addr[it->index++];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, uint16_t skip)
|
||||
{
|
||||
DN_Str8 result {};
|
||||
if (!walk || !arena)
|
||||
return result;
|
||||
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(tmem.arena);
|
||||
DN_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultStr8CRT(DN_StackTraceWalkResult const *walk, uint16_t skip)
|
||||
{
|
||||
DN_Str8 result {};
|
||||
if (!walk)
|
||||
return result;
|
||||
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8Builder builder = DN_Str8Builder_Init(tmem.arena);
|
||||
DN_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = DN_Str8Builder_BuildCRT(&builder);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames(DN_Arena *arena, uint16_t limit)
|
||||
{
|
||||
DN_Slice<DN_StackTraceFrame> result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(tmem.arena, limit);
|
||||
if (!walk.size)
|
||||
return result;
|
||||
|
||||
DN_USize slice_index = 0;
|
||||
result = DN_Slice_Alloc<DN_StackTraceFrame>(arena, walk.size, DN_ZeroMem_No);
|
||||
for (DN_StackTraceWalkResultIterator it = {}; DN_StackTrace_WalkResultIterate(&it, &walk); ) {
|
||||
result.data[slice_index++] = DN_StackTrace_RawFrameToFrame(arena, it.raw_frame);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame(DN_Arena *arena, DN_StackTraceRawFrame raw_frame)
|
||||
{
|
||||
#if defined(DN_OS_WIN32)
|
||||
// NOTE: Get line+filename /////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: Why does zero-initialising this with `line = {};` cause
|
||||
// SymGetLineFromAddr64 function to fail once we are at
|
||||
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
|
||||
// line and file number are still valid in the result which we use, so,
|
||||
// we silently ignore this error.
|
||||
IMAGEHLP_LINEW64 line;
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
DWORD line_displacement = 0;
|
||||
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line)) {
|
||||
line = {};
|
||||
}
|
||||
|
||||
// NOTE: Get function name /////////////////////////////////////////////////////////////////////
|
||||
|
||||
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
|
||||
SYMBOL_INFOW *symbol = DN_CAST(SYMBOL_INFOW *)buffer;
|
||||
symbol->SizeOfStruct = sizeof(*symbol);
|
||||
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
|
||||
|
||||
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
|
||||
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
|
||||
|
||||
// NOTE: Construct result //////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_Str16 file_name16 = DN_Str16{line.FileName, DN_CStr16_Size(line.FileName)};
|
||||
DN_Str16 function_name16 = DN_Str16{symbol->Name, symbol->NameLen};
|
||||
|
||||
DN_StackTraceFrame result = {};
|
||||
result.address = raw_frame.base_addr;
|
||||
result.line_number = line.LineNumber;
|
||||
result.file_name = DN_Win_Str16ToStr8(arena, file_name16);
|
||||
result.function_name = DN_Win_Str16ToStr8(arena, function_name16);
|
||||
|
||||
if (!DN_Str8_HasData(result.function_name))
|
||||
result.function_name = DN_STR8("<unknown function>");
|
||||
if (!DN_Str8_HasData(result.file_name))
|
||||
result.file_name = DN_STR8("<unknown file>");
|
||||
#else
|
||||
DN_StackTraceFrame result = {};
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_StackTrace_Print(uint16_t limit)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Slice<DN_StackTraceFrame> stack_trace = DN_StackTrace_GetFrames(tmem.arena, limit);
|
||||
for (DN_StackTraceFrame &frame : stack_trace)
|
||||
DN_Print_ErrLnF("%.*s(%I64u): %.*s", DN_STR_FMT(frame.file_name), frame.line_number, DN_STR_FMT(frame.function_name));
|
||||
}
|
||||
|
||||
DN_API void DN_StackTrace_ReloadSymbols()
|
||||
{
|
||||
#if defined(DN_OS_WIN32)
|
||||
HANDLE process = GetCurrentProcess();
|
||||
SymRefreshModuleList(process);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: [$DEBG] DN_Debug /////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_API void DN_Debug_TrackAlloc(void *ptr, DN_USize size, bool leak_permitted)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
|
||||
DN_DEFER {
|
||||
DN_TicketMutex_End(&g_dn_core->alloc_table_mutex);
|
||||
};
|
||||
|
||||
// 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.
|
||||
DN_Str8 stack_trace = DN_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
|
||||
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_MakeKeyU64(alloc_table, DN_CAST(uint64_t) ptr);
|
||||
DN_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc_entry.found) {
|
||||
if ((alloc->flags & DN_DebugAllocFlag_Freed) == 0) {
|
||||
DN_Str8 alloc_size = DN_U64ToByteSizeStr8(alloc_table->arena, alloc->size, DN_U64ByteSizeType_Auto);
|
||||
DN_Str8 new_alloc_size = DN_U64ToByteSizeStr8(alloc_table->arena, size, DN_U64ByteSizeType_Auto);
|
||||
DN_HARD_ASSERTF(
|
||||
alloc->flags & DN_DebugAllocFlag_Freed,
|
||||
"This pointer is already in the leak tracker, however it has not "
|
||||
"been freed yet. This same pointer is being ask to be tracked "
|
||||
"twice in the allocation table, e.g. one if its previous free "
|
||||
"calls has not being marked freed with an equivalent call to "
|
||||
"DN_Debug_TrackDealloc()\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is allocating %.*s again at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
,
|
||||
ptr, DN_STR_FMT(alloc_size),
|
||||
DN_STR_FMT(alloc->stack_trace),
|
||||
DN_STR_FMT(new_alloc_size),
|
||||
DN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
// NOTE: Pointer was reused, clean up the prior entry
|
||||
DN_OS_MemRelease(alloc->stack_trace.data, alloc->stack_trace.size);
|
||||
DN_OS_MemRelease(alloc->freed_stack_trace.data, alloc->freed_stack_trace.size);
|
||||
*alloc = {};
|
||||
}
|
||||
|
||||
alloc->ptr = ptr;
|
||||
alloc->size = size;
|
||||
alloc->stack_trace = stack_trace;
|
||||
alloc->flags |= leak_permitted ? DN_DebugAllocFlag_LeakPermitted : 0;
|
||||
}
|
||||
|
||||
DN_API void DN_Debug_TrackDealloc(void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
|
||||
DN_DEFER { DN_TicketMutex_End(&g_dn_core->alloc_table_mutex); };
|
||||
|
||||
DN_Str8 stack_trace = DN_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
|
||||
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_FindKeyU64(alloc_table, DN_CAST(uintptr_t) ptr);
|
||||
DN_HARD_ASSERTF(alloc_entry.found,
|
||||
"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);
|
||||
|
||||
DN_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc->flags & DN_DebugAllocFlag_Freed) {
|
||||
DN_Str8 freed_size = DN_U64ToByteSizeStr8(alloc_table->arena, alloc->freed_size, DN_U64ByteSizeType_Auto);
|
||||
DN_HARD_ASSERTF((alloc->flags & DN_DebugAllocFlag_Freed) == 0,
|
||||
"Double free detected, pointer to free was already marked "
|
||||
"as freed. Either the pointer was reallocated but not "
|
||||
"traced, or, the pointer was freed twice.\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer was freed at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is being freed again at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
,
|
||||
ptr, DN_STR_FMT(freed_size),
|
||||
DN_STR_FMT(alloc->stack_trace),
|
||||
DN_STR_FMT(alloc->freed_stack_trace),
|
||||
DN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
DN_ASSERT(!DN_Str8_HasData(alloc->freed_stack_trace));
|
||||
alloc->flags |= DN_DebugAllocFlag_Freed;
|
||||
alloc->freed_stack_trace = stack_trace;
|
||||
}
|
||||
|
||||
DN_API void DN_Debug_DumpLeaks()
|
||||
{
|
||||
uint64_t leak_count = 0;
|
||||
uint64_t leaked_bytes = 0;
|
||||
for (DN_USize index = 1; index < g_dn_core->alloc_table.occupied; index++) {
|
||||
DN_DSMapSlot<DN_DebugAlloc> *slot = g_dn_core->alloc_table.slots + index;
|
||||
DN_DebugAlloc *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & DN_DebugAllocFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & DN_DebugAllocFlag_LeakPermitted);
|
||||
if (alloc_leaked && !leak_permitted) {
|
||||
leaked_bytes += alloc->size;
|
||||
leak_count++;
|
||||
DN_Str8 alloc_size = DN_U64ToByteSizeStr8(g_dn_core->alloc_table.arena, alloc->size, DN_U64ByteSizeType_Auto);
|
||||
DN_Log_WarningF("Pointer (0x%p) leaked %.*s at:\n"
|
||||
"%.*s",
|
||||
alloc->ptr, DN_STR_FMT(alloc_size),
|
||||
DN_STR_FMT(alloc->stack_trace));
|
||||
}
|
||||
}
|
||||
|
||||
if (leak_count) {
|
||||
DN_Str8 leak_size = DN_U64ToByteSizeStr8(&g_dn_core->arena, leaked_bytes, DN_U64ByteSizeType_Auto);
|
||||
DN_Log_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_STR_FMT(leak_size));
|
||||
}
|
||||
}
|
||||
#endif // DN_LEAK_TRACKING
|
125
dqn_debug.h
125
dqn_debug.h
@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ _____|$$ __$$\ $$ | $$ |$$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$\ $$$$$$$\ |$$ | $$ |$$ |$$$$\
|
||||
// $$ | $$ |$$ __| $$ __$$\ $$ | $$ |$$ |\_$$ |
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ | $$ |
|
||||
// $$$$$$$ |$$$$$$$$\ $$$$$$$ |\$$$$$$ |\$$$$$$ |
|
||||
// \_______/ \________|\_______/ \______/ \______/
|
||||
//
|
||||
// dqn_debug.h -- Tools for debugging
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$ASAN] DN_Asan -- Helpers to manually poison memory using ASAN
|
||||
// [$STKT] DN_StackTrace -- Create stack traces
|
||||
// [$DEBG] DN_Debug -- Allocation leak tracking API
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$ASAN] DN_Asan //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_ASAN_POISON)
|
||||
#define DN_ASAN_POISON 0
|
||||
#endif
|
||||
|
||||
#if !defined(DN_ASAN_VET_POISON)
|
||||
#define DN_ASAN_VET_POISON 0
|
||||
#endif
|
||||
|
||||
#define DN_ASAN_POISON_ALIGNMENT 8
|
||||
|
||||
#if !defined(DN_ASAN_POISON_GUARD_SIZE)
|
||||
#define DN_ASAN_POISON_GUARD_SIZE 128
|
||||
#endif
|
||||
static_assert(DN_IsPowerOfTwoAligned(DN_ASAN_POISON_GUARD_SIZE, DN_ASAN_POISON_ALIGNMENT),
|
||||
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
|
||||
"requirement (8 bytes)");
|
||||
|
||||
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
// NOTE: [$STKT] DN_StackTrace ////////////////////////////////////////////////////////////////////
|
||||
struct DN_StackTraceFrame
|
||||
{
|
||||
uint64_t address;
|
||||
uint64_t line_number;
|
||||
DN_Str8 file_name;
|
||||
DN_Str8 function_name;
|
||||
};
|
||||
|
||||
struct DN_StackTraceRawFrame
|
||||
{
|
||||
void *process;
|
||||
uint64_t base_addr;
|
||||
};
|
||||
|
||||
struct DN_StackTraceWalkResult
|
||||
{
|
||||
void *process; // [Internal] Windows handle to the process
|
||||
uint64_t *base_addr; // The addresses of the functions in the stack trace
|
||||
uint16_t size; // The number of `base_addr`'s stored from the walk
|
||||
};
|
||||
|
||||
struct DN_StackTraceWalkResultIterator
|
||||
{
|
||||
DN_StackTraceRawFrame raw_frame;
|
||||
uint16_t index;
|
||||
};
|
||||
|
||||
// NOTE: [$DEBG] DN_Debug /////////////////////////////////////////////////////////////////////////
|
||||
enum DN_DebugAllocFlag
|
||||
{
|
||||
DN_DebugAllocFlag_Freed = 1 << 0,
|
||||
DN_DebugAllocFlag_LeakPermitted = 1 << 1,
|
||||
};
|
||||
|
||||
struct DN_DebugAlloc
|
||||
{
|
||||
void *ptr; // 8 Pointer to the allocation being tracked
|
||||
DN_USize size; // 16 Size of the allocation
|
||||
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
|
||||
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||
uint16_t flags; // 72 Bit flags from `DN_DebugAllocFlag`
|
||||
};
|
||||
|
||||
static_assert(sizeof(DN_DebugAlloc) == 64 || sizeof(DN_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
|
||||
"We aim to keep the allocation record as light as possible as "
|
||||
"memory tracking can get expensive. Enforce that there is no "
|
||||
"unexpected padding.");
|
||||
|
||||
// NOTE: [$ASAN] DN_Asan //////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_ASAN_PoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
||||
DN_API void DN_ASAN_UnpoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
||||
|
||||
// NOTE: [$STKT] DN_StackTrace ////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (DN_Arena *arena, uint16_t limit);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkStr8CRT (uint16_t limit, uint16_t skip);
|
||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultStr8 (DN_Arena *arena, DN_StackTraceWalkResult const *walk, uint16_t skip);
|
||||
DN_API DN_Str8 DN_StackTrace_WalkResultStr8CRT(DN_StackTraceWalkResult const *walk, uint16_t skip);
|
||||
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames (DN_Arena *arena, uint16_t limit);
|
||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
||||
DN_API void DN_StackTrace_Print (uint16_t limit);
|
||||
DN_API void DN_StackTrace_ReloadSymbols ();
|
||||
|
||||
// NOTE: [$DEBG] DN_Debug /////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_API void DN_Debug_TrackAlloc (void *ptr, DN_USize size, bool alloc_can_leak);
|
||||
DN_API void DN_Debug_TrackDealloc (void *ptr);
|
||||
DN_API void DN_Debug_DumpLeaks ();
|
||||
#else
|
||||
#define DN_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
|
||||
#define DN_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
|
||||
#define DN_Debug_DumpLeaks() do { } while (0)
|
||||
#endif
|
||||
|
||||
|
1207
dqn_docs.cpp
1207
dqn_docs.cpp
File diff suppressed because it is too large
Load Diff
293
dqn_external.h
293
dqn_external.h
@ -1,293 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
// $$ _____|$$ | $$ |\__$$ __|$$ _____|$$ __$$\ $$$\ $$ |$$ __$$\ $$ |
|
||||
// $$ | \$$\ $$ | $$ | $$ | $$ | $$ |$$$$\ $$ |$$ / $$ |$$ |
|
||||
// $$$$$\ \$$$$ / $$ | $$$$$\ $$$$$$$ |$$ $$\$$ |$$$$$$$$ |$$ |
|
||||
// $$ __| $$ $$< $$ | $$ __| $$ __$$< $$ \$$$$ |$$ __$$ |$$ |
|
||||
// $$ | $$ /\$$\ $$ | $$ | $$ | $$ |$$ |\$$$ |$$ | $$ |$$ |
|
||||
// $$$$$$$$\ $$ / $$ | $$ | $$$$$$$$\ $$ | $$ |$$ | \$$ |$$ | $$ |$$$$$$$$\
|
||||
// \________|\__| \__| \__| \________|\__| \__|\__| \__|\__| \__|\________|
|
||||
//
|
||||
// dqn_external.h -- Third party dependencies
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$OS_H] OS Headers ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_OS_WIN32) || defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#if defined(DN_OS_UNIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDONLY ... etc
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/types.h> // pid_t
|
||||
#include <sys/wait.h> // waitpid
|
||||
#include <sys/random.h> // getrandom
|
||||
#include <sys/stat.h> // stat
|
||||
#include <sys/mman.h> // mmap
|
||||
#include <time.h> // clock_gettime, nanosleep
|
||||
#include <unistd.h> // access, gettid, write
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#else
|
||||
#include <sys/sendfile.h> // sendfile
|
||||
#include <linux/fs.h> // FICLONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
|
||||
#endif
|
||||
|
||||
// NOTE: [$STBS] stb_sprintf ///////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_USE_STD_PRINTF)
|
||||
#include <stdio.h>
|
||||
#define DN_SPRINTF(...) sprintf(__VA_ARGS__)
|
||||
#define DN_SNPRINTF(...) snprintf(__VA_ARGS__)
|
||||
#define DN_VSPRINTF(...) vsprintf(__VA_ARGS__)
|
||||
#define DN_VSNPRINTF(...) vsnprintf(__VA_ARGS__)
|
||||
#else
|
||||
#define DN_SPRINTF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
||||
#define DN_SNPRINTF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
||||
#define DN_VSPRINTF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
|
||||
#define DN_VSNPRINTF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
|
||||
|
||||
#if (DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)) && defined(DN_COMPILER_MSVC)
|
||||
#error The STB implementation of sprintf triggers MSVCs implementation of ASAN. Compiling ASAN with STB sprintf is not supported.
|
||||
|
||||
// NOTE: stb_sprintf assumes c-string literals are 4 byte aligned which is
|
||||
// always true, however, reading past the end of a string whose size is not
|
||||
// a multiple of 4 is UB causing ASAN to complain. This is practically safe
|
||||
// and guaranteed by all compilers so we mute this.
|
||||
//
|
||||
// ==12072==ERROR: AddressSanitizer: global-buffer-overflow on address
|
||||
// READ of size 4 at 0x7ff6f442a0d8 thread T0
|
||||
// #0 0x7ff6f42d3be8 in stbsp_vsprintfcb C:\Home\Code\dn\dn_external.cpp:199
|
||||
|
||||
#define STBSP__ASAN __declspec(no_sanitize_address)
|
||||
#endif
|
||||
|
||||
// stb_sprintf - v1.10 - public domain snprintf() implementation
|
||||
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
|
||||
// http://github.com/nothings/stb
|
||||
//
|
||||
// allowed types: sc uidBboXx p AaGgEef n
|
||||
// lengths : hh h ll j z t I64 I32 I
|
||||
//
|
||||
// Contributors:
|
||||
// Fabian "ryg" Giesen (reformatting)
|
||||
// github:aganm (attribute format)
|
||||
//
|
||||
// Contributors (bugfixes):
|
||||
// github:d26435
|
||||
// github:trex78
|
||||
// github:account-login
|
||||
// Jari Komppa (SI suffixes)
|
||||
// Rohit Nirmal
|
||||
// Marcin Wojdyr
|
||||
// Leonard Ritter
|
||||
// Stefano Zanotti
|
||||
// Adam Allison
|
||||
// Arvid Gerstmann
|
||||
// Markus Kolb
|
||||
//
|
||||
// LICENSE:
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_SPRINTF_H_INCLUDE
|
||||
#define STB_SPRINTF_H_INCLUDE
|
||||
|
||||
/*
|
||||
Single file sprintf replacement.
|
||||
|
||||
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
|
||||
Hereby placed in public domain.
|
||||
|
||||
This is a full sprintf replacement that supports everything that
|
||||
the C runtime sprintfs support, including float/double, 64-bit integers,
|
||||
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
|
||||
|
||||
Why would you need this if sprintf already exists? Well, first off,
|
||||
it's *much* faster (see below). It's also much smaller than the CRT
|
||||
versions code-space-wise. We've also added some simple improvements
|
||||
that are super handy (commas in thousands, callbacks at buffer full,
|
||||
for example). Finally, the format strings for MSVC and GCC differ
|
||||
for 64-bit integers (among other small things), so this lets you use
|
||||
the same format strings in cross platform code.
|
||||
|
||||
It uses the standard single file trick of being both the header file
|
||||
and the source itself. If you just include it normally, you just get
|
||||
the header file function definitions. To get the code, you include
|
||||
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
|
||||
|
||||
It only uses va_args macros from the C runtime to do it's work. It
|
||||
does cast doubles to S64s and shifts and divides U64s, which does
|
||||
drag in CRT code on most platforms.
|
||||
|
||||
It compiles to roughly 8K with float support, and 4K without.
|
||||
As a comparison, when using MSVC static libs, calling sprintf drags
|
||||
in 16K.
|
||||
|
||||
API:
|
||||
////
|
||||
int stbsp_sprintf( char * buf, char const * fmt, ... )
|
||||
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
|
||||
Convert an arg list into a buffer. stbsp_snprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
|
||||
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
|
||||
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
|
||||
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
|
||||
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
|
||||
Your callback can then copy the chars out, print them or whatever.
|
||||
This function is actually the workhorse for everything else.
|
||||
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
|
||||
// you return the next buffer to use or 0 to stop converting
|
||||
|
||||
void stbsp_set_separators( char comma, char period )
|
||||
Set the comma and period characters to use.
|
||||
|
||||
FLOATS/DOUBLES:
|
||||
///////////////
|
||||
This code uses a internal float->ascii conversion method that uses
|
||||
doubles with error correction (double-doubles, for ~105 bits of
|
||||
precision). This conversion is round-trip perfect - that is, an atof
|
||||
of the values output here will give you the bit-exact double back.
|
||||
|
||||
One difference is that our insignificant digits will be different than
|
||||
with MSVC or GCC (but they don't match each other either). We also
|
||||
don't attempt to find the minimum length matching float (pre-MSVC15
|
||||
doesn't either).
|
||||
|
||||
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
|
||||
and you'll save 4K of code space.
|
||||
|
||||
64-BIT INTS:
|
||||
////////////
|
||||
This library also supports 64-bit integers and you can use MSVC style or
|
||||
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
|
||||
for size_t and ptr_diff_t (%jd %zd) as well.
|
||||
|
||||
EXTRAS:
|
||||
///////
|
||||
Like some GCCs, for integers and floats, you can use a ' (single quote)
|
||||
specifier and commas will be inserted on the thousands: "%'d" on 12345
|
||||
would print 12,345.
|
||||
|
||||
For integers and floats, you can use a "$" specifier and the number
|
||||
will be converted to float and then divided to get kilo, mega, giga or
|
||||
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
|
||||
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
|
||||
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
|
||||
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
|
||||
suffix, add "_" specifier: "%_$d" -> "2.53M".
|
||||
|
||||
In addition to octal and hexadecimal conversions, you can print
|
||||
integers in binary: "%b" for 256 would print 100.
|
||||
|
||||
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
|
||||
///////////////////////////////////////////////////////////////////
|
||||
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
|
||||
"%24d" across all 32-bit ints (4.5x/4.2x faster)
|
||||
"%x" across all 32-bit ints (4.5x/3.8x faster)
|
||||
"%08x" across all 32-bit ints (4.3x/3.8x faster)
|
||||
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
|
||||
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
|
||||
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
|
||||
"%f" for values near e-300 (7.9x/6.5x faster)
|
||||
"%f" for values near e+300 (10.0x/9.1x faster)
|
||||
"%e" for values near e-300 (10.1x/7.0x faster)
|
||||
"%e" for values near e+300 (9.2x/6.0x faster)
|
||||
"%.320f" for values near e-300 (12.6x/11.2x faster)
|
||||
"%a" for random values (8.6x/4.3x faster)
|
||||
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
|
||||
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
|
||||
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
|
||||
"...512 char string..." ( 35.0x/32.5x faster!)
|
||||
*/
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && defined(__has_attribute)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#if __has_attribute(__no_sanitize__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
|
||||
#elif __has_attribute(__no_sanitize_address__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#elif __has_attribute(__no_address_safety_analysis__)
|
||||
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ASAN
|
||||
#define STBSP__ASAN
|
||||
#endif
|
||||
|
||||
#ifdef STB_SPRINTF_STATIC
|
||||
#define STBSP__PUBLICDEC static
|
||||
#define STBSP__PUBLICDEF static STBSP__ASAN
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#define STBSP__PUBLICDEC extern "C"
|
||||
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
|
||||
#else
|
||||
#define STBSP__PUBLICDEC extern
|
||||
#define STBSP__PUBLICDEF STBSP__ASAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ATTRIBUTE_FORMAT
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBSP__NOTUSED(v) (void)(v)
|
||||
#else
|
||||
#define STBSP__NOTUSED(v) (void)sizeof(v)
|
||||
#endif
|
||||
|
||||
#include <stdarg.h> // for va_arg(), va_list()
|
||||
#include <stddef.h> // size_t, ptrdiff_t
|
||||
|
||||
#ifndef STB_SPRINTF_MIN
|
||||
#define STB_SPRINTF_MIN 512 // how many characters per callback
|
||||
#endif
|
||||
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
|
||||
|
||||
#ifndef STB_SPRINTF_DECORATE
|
||||
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
|
||||
#endif
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
|
||||
#endif // STB_SPRINTF_H_INCLUDE
|
||||
#endif // !defined(DN_USE_STD_PRINTF)
|
1530
dqn_helpers.cpp
1530
dqn_helpers.cpp
File diff suppressed because it is too large
Load Diff
628
dqn_helpers.h
628
dqn_helpers.h
@ -1,628 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
||||
//
|
||||
// dqn_helpers.h -- Helper functions/data structures
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$PCG3] DN_PCG32 -- -- RNG from the PCG family
|
||||
// [$JSON] DN_JSONBuilder -- DN_JSON_BUILDER -- Construct json output
|
||||
// [$BHEX] DN_Bin -- DN_BIN -- Binary <-> hex helpers
|
||||
// [$BSEA] DN_BinarySearch -- -- Binary search
|
||||
// [$BITS] DN_Bit -- -- Bitset manipulation
|
||||
// [$SAFE] DN_Safe -- -- Safe arithmetic, casts, asserts
|
||||
// [$MISC] Misc -- -- Uncategorised helper functions
|
||||
// [$DLIB] DN_Library -- -- Globally shared runtime data for this library
|
||||
// [$PROF] DN_Profiler -- DN_PROFILER -- Profiler that measures using a timestamp counter
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$PCGX] DN_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
struct DN_PCG32 { uint64_t state; };
|
||||
|
||||
#if !defined(DN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] DN_JSONBuilder ///////////////////////////////////////////////////////////////////
|
||||
enum DN_JSONBuilderItem
|
||||
{
|
||||
DN_JSONBuilderItem_Empty,
|
||||
DN_JSONBuilderItem_OpenContainer,
|
||||
DN_JSONBuilderItem_CloseContainer,
|
||||
DN_JSONBuilderItem_KeyValue,
|
||||
};
|
||||
|
||||
struct DN_JSONBuilder
|
||||
{
|
||||
bool use_stdout; // When set, ignore the string builder and dump immediately to stdout
|
||||
DN_Str8Builder string_builder; // (Internal)
|
||||
int indent_level; // (Internal)
|
||||
int spaces_per_indent; // The number of spaces per indent level
|
||||
DN_JSONBuilderItem last_item;
|
||||
};
|
||||
#endif // !defined(DN_NO_JSON_BUIDLER)
|
||||
|
||||
// NOTE: [$BSEA] DN_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
|
||||
template <typename T>
|
||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
|
||||
enum DN_BinarySearchType
|
||||
{
|
||||
// Index of the match. If no match is found, found is set to false and the
|
||||
// index is set to the index where the match should be inserted/exist, if
|
||||
// it were in the array
|
||||
DN_BinarySearchType_Match,
|
||||
|
||||
// Index of the first element in the array that is `element >= find`. If no such
|
||||
// item is found or the array is empty, then, the index is set to the array
|
||||
// size and found is set to `false`.
|
||||
//
|
||||
// For example:
|
||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ARRAY_UCOUNT(array), 4, DN_BinarySearchType_LowerBound);
|
||||
// printf("%zu\n", result.index); // Prints index '4'
|
||||
|
||||
DN_BinarySearchType_LowerBound,
|
||||
|
||||
// Index of the first element in the array that is `element > find`. If no such
|
||||
// item is found or the array is empty, then, the index is set to the array
|
||||
// size and found is set to `false`.
|
||||
//
|
||||
// For example:
|
||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ARRAY_UCOUNT(array), 4, DN_BinarySearchType_UpperBound);
|
||||
// printf("%zu\n", result.index); // Prints index '5'
|
||||
|
||||
DN_BinarySearchType_UpperBound,
|
||||
};
|
||||
|
||||
struct DN_BinarySearchResult
|
||||
{
|
||||
bool found;
|
||||
DN_USize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using DN_QSortLessThanProc = bool(T const &a, T const &b, void *user_context);
|
||||
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_U64Str8
|
||||
{
|
||||
char data[27+1]; // NOTE(dn): 27 is the maximum size of uint64_t including a separator
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
enum DN_U64ByteSizeType
|
||||
{
|
||||
DN_U64ByteSizeType_B,
|
||||
DN_U64ByteSizeType_KiB,
|
||||
DN_U64ByteSizeType_MiB,
|
||||
DN_U64ByteSizeType_GiB,
|
||||
DN_U64ByteSizeType_TiB,
|
||||
DN_U64ByteSizeType_Count,
|
||||
DN_U64ByteSizeType_Auto,
|
||||
};
|
||||
|
||||
struct DN_U64ByteSize
|
||||
{
|
||||
DN_U64ByteSizeType type;
|
||||
DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
|
||||
DN_F64 bytes;
|
||||
};
|
||||
|
||||
typedef uint32_t DN_U64AgeUnit;
|
||||
enum DN_U64AgeUnit_
|
||||
{
|
||||
DN_U64AgeUnit_Sec = 1 << 0,
|
||||
DN_U64AgeUnit_Min = 1 << 1,
|
||||
DN_U64AgeUnit_Hr = 1 << 2,
|
||||
DN_U64AgeUnit_Day = 1 << 3,
|
||||
DN_U64AgeUnit_Week = 1 << 4,
|
||||
DN_U64AgeUnit_Year = 1 << 5,
|
||||
DN_U64AgeUnit_HMS = DN_U64AgeUnit_Sec | DN_U64AgeUnit_Min | DN_U64AgeUnit_Hr,
|
||||
DN_U64AgeUnit_All = DN_U64AgeUnit_HMS | DN_U64AgeUnit_Day | DN_U64AgeUnit_Week | DN_U64AgeUnit_Year,
|
||||
};
|
||||
|
||||
struct DN_U64HexStr8
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
typedef uint32_t DN_U64HexStr8Flags;
|
||||
enum DN_U64HexStr8Flags_
|
||||
{
|
||||
DN_HexU64Str8Flags_Nil = 0,
|
||||
DN_HexU64Str8Flags_0xPrefix = 1 << 0, /// Add the '0x' prefix from the string
|
||||
DN_HexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
// NOTE: [$PROF] DN_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_PROFILER_ANCHOR_BUFFER_SIZE)
|
||||
#define DN_PROFILER_ANCHOR_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
struct DN_ProfilerAnchor
|
||||
{
|
||||
// Inclusive refers to the time spent to complete the function call
|
||||
// including all children functions.
|
||||
//
|
||||
// Exclusive refers to the time spent in the function, not including any
|
||||
// time spent in children functions that we call that are also being
|
||||
// profiled. If we recursively call into ourselves, the time we spent in
|
||||
// our function is accumulated.
|
||||
uint64_t tsc_inclusive;
|
||||
uint64_t tsc_exclusive;
|
||||
uint16_t hit_count;
|
||||
DN_Str8 name;
|
||||
};
|
||||
|
||||
struct DN_ProfilerZone
|
||||
{
|
||||
uint16_t anchor_index;
|
||||
uint64_t begin_tsc;
|
||||
uint16_t parent_zone;
|
||||
uint64_t elapsed_tsc_at_zone_start;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
struct DN_ProfilerZoneScope
|
||||
{
|
||||
DN_ProfilerZoneScope(DN_Str8 name, uint16_t anchor_index);
|
||||
~DN_ProfilerZoneScope();
|
||||
DN_ProfilerZone zone;
|
||||
};
|
||||
#define DN_Profiler_ZoneScopeAtIndex(name, anchor_index) auto DN_UNIQUE_NAME(profile_zone_) = DN_ProfilerZoneScope(DN_STR8(name), anchor_index)
|
||||
#define DN_Profiler_ZoneScope(name) DN_Profiler_ZoneScopeAtIndex(name, __COUNTER__ + 1)
|
||||
#endif
|
||||
|
||||
#define DN_Profiler_ZoneBlockIndex(name, index) \
|
||||
for (DN_ProfilerZone DN_UNIQUE_NAME(profile_zone__) = DN_Profiler_BeginZoneAtIndex(name, index), DN_UNIQUE_NAME(dummy__) = {}; \
|
||||
DN_UNIQUE_NAME(dummy__).begin_tsc == 0; \
|
||||
DN_Profiler_EndZone(DN_UNIQUE_NAME(profile_zone__)), DN_UNIQUE_NAME(dummy__).begin_tsc = 1)
|
||||
|
||||
#define DN_Profiler_ZoneBlock(name) DN_Profiler_ZoneBlockIndex(DN_STR8(name), __COUNTER__ + 1)
|
||||
|
||||
enum DN_ProfilerAnchorBuffer
|
||||
{
|
||||
DN_ProfilerAnchorBuffer_Back,
|
||||
DN_ProfilerAnchorBuffer_Front,
|
||||
};
|
||||
|
||||
struct DN_Profiler
|
||||
{
|
||||
DN_ProfilerAnchor anchors[2][DN_PROFILER_ANCHOR_BUFFER_SIZE];
|
||||
uint8_t active_anchor_buffer;
|
||||
uint16_t parent_zone;
|
||||
};
|
||||
#endif // !defined(DN_NO_PROFILER)
|
||||
|
||||
// NOTE: [$JOBQ] DN_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
typedef void (DN_JobQueueFunc)(DN_OSThread *thread, void *user_context);
|
||||
struct DN_Job
|
||||
{
|
||||
DN_JobQueueFunc *func; // The function to invoke for the job
|
||||
void *user_context; // Pointer user can set to use in their `job_func`
|
||||
uint64_t elapsed_tsc;
|
||||
uint16_t user_tag; // Arbitrary value the user can set to identiy the type of `user_context` this job has
|
||||
bool add_to_completion_queue; // When true, on job completion, job must be dequeued from the completion queue via `GetFinishedJobs`
|
||||
};
|
||||
|
||||
#if !defined(DN_JOB_QUEUE_SPMC_SIZE)
|
||||
#define DN_JOB_QUEUE_SPMC_SIZE 128
|
||||
#endif
|
||||
|
||||
struct DN_JobQueueSPMC
|
||||
{
|
||||
DN_OSMutex mutex;
|
||||
DN_OSSemaphore thread_wait_for_job_semaphore;
|
||||
DN_OSSemaphore wait_for_completion_semaphore;
|
||||
DN_U32 threads_waiting_for_completion;
|
||||
|
||||
DN_Job jobs[DN_JOB_QUEUE_SPMC_SIZE];
|
||||
DN_B32 quit;
|
||||
DN_U32 quit_exit_code;
|
||||
DN_U32 volatile read_index;
|
||||
DN_U32 volatile finish_index;
|
||||
DN_U32 volatile write_index;
|
||||
|
||||
DN_OSSemaphore complete_queue_write_semaphore;
|
||||
DN_Job complete_queue[DN_JOB_QUEUE_SPMC_SIZE];
|
||||
DN_U32 volatile complete_read_index;
|
||||
DN_U32 volatile complete_write_index;
|
||||
};
|
||||
|
||||
// NOTE: [$CORE] DN_Core //////////////////////////////////////////////////////////////////////////
|
||||
// Book-keeping data for the library and allow customisation of certain features
|
||||
// provided.
|
||||
struct DN_Core
|
||||
{
|
||||
bool init; // True if the library has been initialised via `DN_Library_Init`
|
||||
DN_OSMutex init_mutex;
|
||||
DN_Str8 exe_dir; // The directory of the current executable
|
||||
DN_Arena arena;
|
||||
DN_Pool pool;
|
||||
DN_ArenaCatalog arena_catalog;
|
||||
bool slow_verification_checks; // Enable expensive library verification checks
|
||||
DN_CPUReport cpu_report;
|
||||
DN_TLS tls; // Thread local storage state for the main thread.
|
||||
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_LogProc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data; // User pointer passed into 'log_callback'
|
||||
bool log_to_file; // Output logs to file as well as standard out
|
||||
DN_OSFile log_file; // TODO(dn): Hmmm, how should we do this... ?
|
||||
DN_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
|
||||
|
||||
// NOTE: Memory ////////////////////////////////////////////////////////////////////////////////
|
||||
// Total OS mem allocs in lifetime of program (e.g. malloc, VirtualAlloc, HeapAlloc ...). This
|
||||
// only includes allocations routed through the library such as the growing nature of arenas or
|
||||
// using the memory allocation routines in the library like DN_OS_MemCommit and so forth.
|
||||
uint64_t mem_allocs_total;
|
||||
uint64_t mem_allocs_frame; // Total OS mem allocs since the last 'DN_Core_FrameBegin' was invoked
|
||||
|
||||
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
DN_DSMap<DN_DebugAlloc> alloc_table;
|
||||
DN_TicketMutex alloc_table_mutex;
|
||||
DN_Arena alloc_table_arena;
|
||||
#endif
|
||||
|
||||
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_OS_WIN32)
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
DN_TicketMutex win32_bcrypt_rng_mutex;
|
||||
void * win32_bcrypt_rng_handle;
|
||||
bool win32_sym_initialised;
|
||||
#endif
|
||||
|
||||
// NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
|
||||
uint32_t os_page_size;
|
||||
uint32_t os_alloc_granularity;
|
||||
|
||||
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_Profiler * profiler;
|
||||
DN_Profiler profiler_default_instance;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DN_CoreOnInit
|
||||
{
|
||||
DN_CoreOnInit_Nil = 0,
|
||||
DN_CoreOnInit_LogLibFeatures = 1 << 0,
|
||||
DN_CoreOnInit_LogCPUFeatures = 1 << 1,
|
||||
DN_CoreOnInit_LogAllFeatures = DN_CoreOnInit_LogLibFeatures | DN_CoreOnInit_LogCPUFeatures,
|
||||
};
|
||||
|
||||
// NOTE: [$PCGX] DN_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_PCG32 DN_PCG32_Init (uint64_t seed);
|
||||
DN_API uint32_t DN_PCG32_Next (DN_PCG32 *rng);
|
||||
DN_API uint64_t DN_PCG32_Next64 (DN_PCG32 *rng);
|
||||
DN_API uint32_t DN_PCG32_Range (DN_PCG32 *rng, uint32_t low, uint32_t high);
|
||||
DN_API DN_F32 DN_PCG32_NextF32 (DN_PCG32 *rng);
|
||||
DN_API DN_F64 DN_PCG32_NextF64 (DN_PCG32 *rng);
|
||||
DN_API void DN_PCG32_Advance (DN_PCG32 *rng, uint64_t delta);
|
||||
|
||||
#if !defined(DN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] DN_JSONBuilder ///////////////////////////////////////////////////////////////////
|
||||
#define DN_JSONBuilder_Object(builder) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBegin(builder), \
|
||||
DN_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
DN_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_Array(builder) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBegin(builder), \
|
||||
DN_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define DN_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
DN_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
|
||||
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);
|
||||
DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...);
|
||||
DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
||||
DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder);
|
||||
DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
||||
DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder);
|
||||
DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
||||
DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value);
|
||||
DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value);
|
||||
DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places);
|
||||
DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value);
|
||||
|
||||
#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_STR8(""))
|
||||
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_STR8(""))
|
||||
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_STR8(""), value)
|
||||
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_STR8(""), value)
|
||||
#endif // !defined(DN_NO_JSON_BUILDER)
|
||||
|
||||
// NOTE: [$BSEA] DN_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array,
|
||||
DN_USize array_size,
|
||||
T const &find,
|
||||
DN_BinarySearchType type = DN_BinarySearchType_Match,
|
||||
DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
||||
|
||||
// NOTE: [$QSOR] DN_QSort /////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> void DN_QSort (T *array,
|
||||
DN_USize array_size,
|
||||
void *user_context,
|
||||
DN_QSortLessThanProc<T> less_than = DN_QSort_DefaultLessThan);
|
||||
|
||||
// NOTE: [$BITS] DN_Bit ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_Bit_UnsetInplace (DN_USize *flags, DN_USize bitfield);
|
||||
DN_API void DN_Bit_SetInplace (DN_USize *flags, DN_USize bitfield);
|
||||
DN_API bool DN_Bit_IsSet (DN_USize bits, DN_USize bits_to_set);
|
||||
DN_API bool DN_Bit_IsNotSet (DN_USize bits, DN_USize bits_to_check);
|
||||
#define DN_Bit_ClearNextLSB(value) (value) & ((value) - 1)
|
||||
|
||||
// NOTE: [$SAFE] DN_Safe //////////////////////////////////////////////////////////////////////////
|
||||
DN_API int64_t DN_Safe_AddI64 (int64_t a, int64_t b);
|
||||
DN_API int64_t DN_Safe_MulI64 (int64_t a, int64_t b);
|
||||
|
||||
DN_API uint64_t DN_Safe_AddU64 (uint64_t a, uint64_t b);
|
||||
DN_API uint64_t DN_Safe_MulU64 (uint64_t a, uint64_t b);
|
||||
|
||||
DN_API uint64_t DN_Safe_SubU64 (uint64_t a, uint64_t b);
|
||||
DN_API uint32_t DN_Safe_SubU32 (uint32_t a, uint32_t b);
|
||||
|
||||
DN_API int DN_Safe_SaturateCastUSizeToInt (DN_USize val);
|
||||
DN_API int8_t DN_Safe_SaturateCastUSizeToI8 (DN_USize val);
|
||||
DN_API int16_t DN_Safe_SaturateCastUSizeToI16 (DN_USize val);
|
||||
DN_API int32_t DN_Safe_SaturateCastUSizeToI32 (DN_USize val);
|
||||
DN_API int64_t DN_Safe_SaturateCastUSizeToI64 (DN_USize val);
|
||||
|
||||
DN_API int DN_Safe_SaturateCastU64ToInt (uint64_t val);
|
||||
DN_API int8_t DN_Safe_SaturateCastU8ToI8 (uint64_t val);
|
||||
DN_API int16_t DN_Safe_SaturateCastU16ToI16 (uint64_t val);
|
||||
DN_API int32_t DN_Safe_SaturateCastU32ToI32 (uint64_t val);
|
||||
DN_API int64_t DN_Safe_SaturateCastU64ToI64 (uint64_t val);
|
||||
DN_API unsigned int DN_Safe_SaturateCastU64ToUInt (uint64_t val);
|
||||
DN_API uint8_t DN_Safe_SaturateCastU64ToU8 (uint64_t val);
|
||||
DN_API uint16_t DN_Safe_SaturateCastU64ToU16 (uint64_t val);
|
||||
DN_API uint32_t DN_Safe_SaturateCastU64ToU32 (uint64_t val);
|
||||
|
||||
DN_API uint8_t DN_Safe_SaturateCastUSizeToU8 (DN_USize val);
|
||||
DN_API uint16_t DN_Safe_SaturateCastUSizeToU16 (DN_USize val);
|
||||
DN_API uint32_t DN_Safe_SaturateCastUSizeToU32 (DN_USize val);
|
||||
DN_API uint64_t DN_Safe_SaturateCastUSizeToU64 (DN_USize val);
|
||||
|
||||
DN_API int DN_Safe_SaturateCastISizeToInt (DN_ISize val);
|
||||
DN_API int8_t DN_Safe_SaturateCastISizeToI8 (DN_ISize val);
|
||||
DN_API int16_t DN_Safe_SaturateCastISizeToI16 (DN_ISize val);
|
||||
DN_API int32_t DN_Safe_SaturateCastISizeToI32 (DN_ISize val);
|
||||
DN_API int64_t DN_Safe_SaturateCastISizeToI64 (DN_ISize val);
|
||||
|
||||
DN_API unsigned int DN_Safe_SaturateCastISizeToUInt (DN_ISize val);
|
||||
DN_API uint8_t DN_Safe_SaturateCastISizeToU8 (DN_ISize val);
|
||||
DN_API uint16_t DN_Safe_SaturateCastISizeToU16 (DN_ISize val);
|
||||
DN_API uint32_t DN_Safe_SaturateCastISizeToU32 (DN_ISize val);
|
||||
DN_API uint64_t DN_Safe_SaturateCastISizeToU64 (DN_ISize val);
|
||||
|
||||
DN_API DN_ISize DN_Safe_SaturateCastI64ToISize (int64_t val);
|
||||
DN_API int8_t DN_Safe_SaturateCastI64ToI8 (int64_t val);
|
||||
DN_API int16_t DN_Safe_SaturateCastI64ToI16 (int64_t val);
|
||||
DN_API int32_t DN_Safe_SaturateCastI64ToI32 (int64_t val);
|
||||
|
||||
DN_API unsigned int DN_Safe_SaturateCastI64ToUInt (int64_t val);
|
||||
DN_API DN_ISize DN_Safe_SaturateCastI64ToUSize (int64_t val);
|
||||
DN_API uint8_t DN_Safe_SaturateCastI64ToU8 (int64_t val);
|
||||
DN_API uint16_t DN_Safe_SaturateCastI64ToU16 (int64_t val);
|
||||
DN_API uint32_t DN_Safe_SaturateCastI64ToU32 (int64_t val);
|
||||
DN_API uint64_t DN_Safe_SaturateCastI64ToU64 (int64_t val);
|
||||
|
||||
DN_API int8_t DN_Safe_SaturateCastIntToI8 (int val);
|
||||
DN_API int16_t DN_Safe_SaturateCastIntToI16 (int val);
|
||||
DN_API uint8_t DN_Safe_SaturateCastIntToU8 (int val);
|
||||
DN_API uint16_t DN_Safe_SaturateCastIntToU16 (int val);
|
||||
DN_API uint32_t DN_Safe_SaturateCastIntToU32 (int val);
|
||||
DN_API uint64_t DN_Safe_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
DN_API int DN_FmtBuffer3DotTruncate (char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_U64Str8 DN_U64ToStr8 (uint64_t val, char separator);
|
||||
DN_API DN_U64ByteSize DN_U64ToByteSize (uint64_t bytes, DN_U64ByteSizeType type);
|
||||
DN_API DN_Str8 DN_U64ToByteSizeStr8 (DN_Arena *arena, uint64_t bytes, DN_U64ByteSizeType desired_type);
|
||||
DN_API DN_Str8 DN_U64ByteSizeTypeString (DN_U64ByteSizeType type);
|
||||
DN_API DN_Str8 DN_U64ToAge (DN_Arena *arena, uint64_t age_s, DN_U64AgeUnit unit);
|
||||
DN_API DN_Str8 DN_F64ToAge (DN_Arena *arena, DN_F64 age_s, DN_U64AgeUnit unit);
|
||||
|
||||
DN_API uint64_t DN_HexToU64 (DN_Str8 hex);
|
||||
DN_API DN_Str8 DN_U64ToHex (DN_Arena *arena, uint64_t number, DN_U64HexStr8Flags flags);
|
||||
DN_API DN_U64HexStr8 DN_U64ToHexStr8 (uint64_t number, uint32_t flags);
|
||||
|
||||
DN_API bool DN_BytesToHexPtr (void const *src, DN_USize src_size, char *dest);
|
||||
DN_API DN_Str8 DN_BytesToHex (DN_Arena *arena, void const *src, DN_USize size);
|
||||
#define DN_BytesToHex_TLS(...) DN_BytesToHex(DN_TLS_TopArena(), __VA_ARGS__)
|
||||
|
||||
DN_API DN_USize DN_HexToBytesPtrUnchecked (DN_Str8 hex, void *dest, DN_USize dest_size);
|
||||
DN_API DN_USize DN_HexToBytesPtr (DN_Str8 hex, void *dest, DN_USize dest_size);
|
||||
DN_API DN_Str8 DN_HexToBytesUnchecked (DN_Arena *arena, DN_Str8 hex);
|
||||
#define DN_HexToBytesUnchecked_TLS(...) DN_HexToBytesUnchecked(DN_TLS_TopArena(), __VA_ARGS__)
|
||||
DN_API DN_Str8 DN_HexToBytes (DN_Arena *arena, DN_Str8 hex);
|
||||
#define DN_HexToBytes_TLS(...) DN_HexToBytes(DN_TLS_TopArena(), __VA_ARGS__)
|
||||
|
||||
// NOTE: [$PROF] DN_Profiler //////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_ReadBuffer ();
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_WriteBuffer ();
|
||||
#define DN_Profiler_BeginZone(name) DN_Profiler_BeginZoneAtIndex(DN_STR8(name), __COUNTER__ + 1)
|
||||
DN_API DN_ProfilerZone DN_Profiler_BeginZoneAtIndex (DN_Str8 name, uint16_t anchor_index);
|
||||
DN_API void DN_Profiler_EndZone (DN_ProfilerZone zone);
|
||||
DN_API DN_ProfilerAnchor * DN_Profiler_AnchorBuffer (DN_ProfilerAnchorBuffer buffer);
|
||||
DN_API void DN_Profiler_SwapAnchorBuffer ();
|
||||
DN_API void DN_Profiler_Dump (uint64_t tsc_per_second);
|
||||
|
||||
// NOTE: [$JOBQ] DN_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_JobQueueSPMC DN_OS_JobQueueSPMCInit ();
|
||||
DN_API bool DN_OS_JobQueueSPMCCanAdd (DN_JobQueueSPMC const *queue, uint32_t count);
|
||||
DN_API bool DN_OS_JobQueueSPMCAddArray (DN_JobQueueSPMC *queue, DN_Job *jobs, uint32_t count);
|
||||
DN_API bool DN_OS_JobQueueSPMCAdd (DN_JobQueueSPMC *queue, DN_Job job);
|
||||
DN_API void DN_OS_JobQueueSPMCWaitForCompletion (DN_JobQueueSPMC *queue);
|
||||
DN_API int32_t DN_OS_JobQueueSPMCThread (DN_OSThread *thread);
|
||||
DN_API DN_USize DN_OS_JobQueueSPMCGetFinishedJobs (DN_JobQueueSPMC *queue, DN_Job *jobs, DN_USize jobs_size);
|
||||
|
||||
// NOTE: DN_Core ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_Core_Init (DN_Core *core, DN_CoreOnInit on_init);
|
||||
DN_API void DN_Core_BeginFrame ();
|
||||
DN_API void DN_Core_SetPointer (DN_Core *core);
|
||||
#if !defined(DN_NO_PROFILER)
|
||||
DN_API void DN_Core_SetProfiler (DN_Profiler *profiler);
|
||||
#endif
|
||||
DN_API void DN_Core_SetLogCallback (DN_LogProc *proc, void *user_data);
|
||||
DN_API void DN_Core_DumpThreadContextArenaStat (DN_Str8 file_path);
|
||||
DN_API DN_Arena * DN_Core_AllocArenaF (DN_USize reserve, DN_USize commit, uint8_t arena_flags, char const *fmt, ...);
|
||||
DN_API bool DN_Core_EraseArena (DN_Arena *arena, DN_ArenaCatalogFreeArena free_arena);
|
||||
|
||||
// NOTE: [$BSEA] DN_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
||||
DN_USize array_size,
|
||||
T const &find,
|
||||
DN_BinarySearchType type,
|
||||
DN_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
DN_BinarySearchResult result = {};
|
||||
if (!array || array_size <= 0 || !less_than)
|
||||
return result;
|
||||
|
||||
T const *end = array + array_size;
|
||||
T const *first = array;
|
||||
T const *last = end;
|
||||
while (first != last) {
|
||||
DN_USize count = last - first;
|
||||
T const *it = first + (count / 2);
|
||||
|
||||
bool advance_first = false;
|
||||
if (type == DN_BinarySearchType_UpperBound)
|
||||
advance_first = !less_than(find, it[0]);
|
||||
else
|
||||
advance_first = less_than(it[0], find);
|
||||
|
||||
if (advance_first)
|
||||
first = it + 1;
|
||||
else
|
||||
last = it;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DN_BinarySearchType_Match: {
|
||||
result.found = first != end && !less_than(find, *first);
|
||||
} break;
|
||||
|
||||
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||
case DN_BinarySearchType_UpperBound: {
|
||||
result.found = first != end;
|
||||
} break;
|
||||
}
|
||||
|
||||
result.index = first - array;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$QSOR] DN_QSort /////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context)
|
||||
{
|
||||
(void)user_context;
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_QSort(T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc<T> less_than)
|
||||
{
|
||||
if (!array || array_size <= 1 || !less_than)
|
||||
return;
|
||||
|
||||
// NOTE: Insertion Sort, under 24->32 is an optimal amount /////////////////////////////////////
|
||||
const DN_USize QSORT_THRESHOLD = 24;
|
||||
if (array_size < QSORT_THRESHOLD) {
|
||||
for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) {
|
||||
for (DN_USize index = 0; index < item_to_insert_index; index++) {
|
||||
if (!less_than(array[index], array[item_to_insert_index], user_context)) {
|
||||
T item_to_insert = array[item_to_insert_index];
|
||||
for (DN_USize i = item_to_insert_index; i > index; i--)
|
||||
array[i] = array[i - 1];
|
||||
|
||||
array[index] = item_to_insert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Quick sort, under 24->32 is an optimal amount /////////////////////////////////////////
|
||||
DN_USize last_index = array_size - 1;
|
||||
DN_USize pivot_index = array_size / 2;
|
||||
DN_USize partition_index = 0;
|
||||
DN_USize start_index = 0;
|
||||
|
||||
// Swap pivot with last index, so pivot is always at the end of the array.
|
||||
// This makes logic much simpler.
|
||||
DN_SWAP(array[last_index], array[pivot_index]);
|
||||
pivot_index = last_index;
|
||||
|
||||
// 4^, 8, 7, 5, 2, 3, 6
|
||||
if (less_than(array[start_index], array[pivot_index], user_context))
|
||||
partition_index++;
|
||||
start_index++;
|
||||
|
||||
// 4, |8, 7, 5^, 2, 3, 6*
|
||||
// 4, 5, |7, 8, 2^, 3, 6*
|
||||
// 4, 5, 2, |8, 7, ^3, 6*
|
||||
// 4, 5, 2, 3, |7, 8, ^6*
|
||||
for (DN_USize index = start_index; index < last_index; index++) {
|
||||
if (less_than(array[index], array[pivot_index], user_context)) {
|
||||
DN_SWAP(array[partition_index], array[index]);
|
||||
partition_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Move pivot to right of partition
|
||||
// 4, 5, 2, 3, |6, 8, ^7*
|
||||
DN_SWAP(array[partition_index], array[pivot_index]);
|
||||
DN_QSort(array, partition_index, user_context, less_than);
|
||||
|
||||
// Skip the value at partion index since that is guaranteed to be sorted.
|
||||
// 4, 5, 2, 3, (x), 8, 7
|
||||
DN_USize one_after_partition_index = partition_index + 1;
|
||||
DN_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than);
|
||||
}
|
1572
dqn_math.cpp
1572
dqn_math.cpp
File diff suppressed because it is too large
Load Diff
544
dqn_os.cpp
544
dqn_os.cpp
@ -1,544 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__|
|
||||
// $$ | $$ |\$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ |$$\ $$ |
|
||||
// $$$$$$ |\$$$$$$ |
|
||||
// \______/ \______/
|
||||
//
|
||||
// dqn_os.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8(DN_OSDateTime time, char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTimeStr8 result = {};
|
||||
result.hms_size = DN_CAST(uint8_t) DN_SNPRINTF(result.hms,
|
||||
DN_ARRAY_ICOUNT(result.hms),
|
||||
"%02hhu%c%02hhu%c%02hhu",
|
||||
time.hour,
|
||||
hms_separator,
|
||||
time.minutes,
|
||||
hms_separator,
|
||||
time.seconds);
|
||||
|
||||
result.date_size = DN_CAST(uint8_t) DN_SNPRINTF(result.date,
|
||||
DN_ARRAY_ICOUNT(result.date),
|
||||
"%hu%c%02hhu%c%02hhu",
|
||||
time.year,
|
||||
date_separator,
|
||||
time.month,
|
||||
date_separator,
|
||||
time.day);
|
||||
|
||||
DN_ASSERT(result.hms_size < DN_ARRAY_UCOUNT(result.hms));
|
||||
DN_ASSERT(result.date_size < DN_ARRAY_UCOUNT(result.date));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTime time = DN_OS_DateLocalTimeNow();
|
||||
DN_OSDateTimeStr8 result = DN_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_OS_DateUnixTimeS()
|
||||
{
|
||||
uint64_t result = DN_OS_DateUnixTimeNs() / (1'000 /*us*/ * 1'000 /*ms*/ * 1'000 /*s*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_DateIsValid(DN_OSDateTime date)
|
||||
{
|
||||
if (date.year < 1970)
|
||||
return false;
|
||||
if (date.month <= 0 || date.month >= 13)
|
||||
return false;
|
||||
if (date.day <= 0 || date.day >= 32)
|
||||
return false;
|
||||
if (date.hour >= 24)
|
||||
return false;
|
||||
if (date.minutes >= 60)
|
||||
return false;
|
||||
if (date.seconds >= 60)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
DN_Str8 exe_path = DN_OS_EXEPath(tmem.arena);
|
||||
DN_Str8 separators[] = {DN_STR8("/"), DN_STR8("\\")};
|
||||
DN_Str8BinarySplitResult split = DN_Str8_BinarySplitLastArray(exe_path, separators, DN_ARRAY_UCOUNT(separators));
|
||||
result = DN_Str8_Copy(arena, split.lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = ticks / DN_CAST(DN_F64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000) / DN_CAST(DN_F64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000) / DN_CAST(DN_F64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000'000) / DN_CAST(DN_F64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin()
|
||||
{
|
||||
DN_OSTimer result = {};
|
||||
result.start = DN_OS_PerfCounterNow();
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TimerEnd(DN_OSTimer *timer)
|
||||
{
|
||||
timer->end = DN_OS_PerfCounterNow();
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerS(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterS(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerMs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterMs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerUs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterUs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerNs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterNs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency)
|
||||
{
|
||||
uint64_t os_frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL;
|
||||
uint64_t tsc_begin = DN_CPU_TSC();
|
||||
uint64_t result = 0;
|
||||
if (tsc_begin) {
|
||||
uint64_t os_elapsed = 0;
|
||||
for (uint64_t os_begin = DN_OS_PerfCounterNow(); os_elapsed < os_target_elapsed; )
|
||||
os_elapsed = DN_OS_PerfCounterNow() - os_begin;
|
||||
uint64_t tsc_end = DN_CPU_TSC();
|
||||
uint64_t tsc_elapsed = tsc_end - tsc_begin;
|
||||
result = tsc_elapsed / os_elapsed * os_frequency;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: [$FILE] DN_OSPathInfo/File ///////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against)
|
||||
{
|
||||
DN_OSPathInfo file_info = DN_OS_PathInfo(file);
|
||||
DN_OSPathInfo check_against_info = DN_OS_PathInfo(check_against);
|
||||
bool result = !file_info.exists || file_info.last_write_time_in_s < check_against_info.last_write_time_in_s;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWrite(DN_OSFile *file, DN_Str8 buffer, DN_ErrSink *error)
|
||||
{
|
||||
bool result = DN_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteFV(DN_OSFile *file, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!file || !fmt)
|
||||
return result;
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
result = DN_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteF(DN_OSFile *file, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_FileWriteFV(file, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll(DN_Arena *arena, DN_Str8 path, DN_ErrSink *error)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
// NOTE: Query file size + allocate buffer /////////////////////////////////////////////////////
|
||||
DN_OSPathInfo path_info = DN_OS_PathInfo(path);
|
||||
if (!path_info.exists) {
|
||||
DN_ErrSink_AppendF(error, 1, "File does not exist/could not be queried for reading '%.*s'", DN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_ArenaTempMem temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
result = DN_Str8_Alloc(arena, path_info.size, DN_ZeroMem_No);
|
||||
if (!DN_Str8_HasData(result)) {
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 buffer_size_str8 = DN_U64ToByteSizeStr8(tmem.arena, path_info.size, DN_U64ByteSizeType_Auto);
|
||||
DN_ErrSink_AppendF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_STR_FMT(buffer_size_str8), DN_STR_FMT(path));
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Read the file from disk ///////////////////////////////////////////////////////////////
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_Read, error);
|
||||
bool read_failed = !DN_OS_FileRead(&file, result.data, result.size, error);
|
||||
if (file.error || read_failed) {
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
}
|
||||
DN_OS_FileClose(&file);
|
||||
|
||||
return result;
|
||||
}
|
||||
DN_API bool DN_OS_WriteAll(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error)
|
||||
{
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_Write, error);
|
||||
bool result = DN_OS_FileWrite(&file, buffer, error);
|
||||
DN_OS_FileClose(&file);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllFV(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAll(file_path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllF(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllFV(file_path, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 tmp_path = DN_Str8_InitF(tmem.arena, "%.*s.tmp", DN_STR_FMT(path));
|
||||
if (!DN_OS_WriteAll(tmp_path, buffer, error))
|
||||
return false;
|
||||
if (!DN_OS_CopyFile(tmp_path, path, true /*overwrite*/, error))
|
||||
return false;
|
||||
if (!DN_OS_PathDelete(tmp_path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeFV(DN_Str8 path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAllSafe(path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeF(DN_Str8 path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllSafeFV(path, error, fmt, args);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: [$PATH] DN_OSPath ////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
if (!arena || !fs_path || !DN_Str8_HasData(path))
|
||||
return false;
|
||||
|
||||
if (path.size <= 0)
|
||||
return true;
|
||||
|
||||
DN_Str8 const delimiter_array[] = {
|
||||
DN_STR8("\\"),
|
||||
DN_STR8("/")
|
||||
};
|
||||
|
||||
if (fs_path->links_size == 0) {
|
||||
fs_path->has_prefix_path_separator = (path.data[0] == '/');
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
DN_Str8BinarySplitResult delimiter = DN_Str8_BinarySplitArray(path, delimiter_array, DN_ARRAY_UCOUNT(delimiter_array));
|
||||
for (; delimiter.lhs.data; delimiter = DN_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DN_ARRAY_UCOUNT(delimiter_array))) {
|
||||
if (delimiter.lhs.size <= 0)
|
||||
continue;
|
||||
|
||||
DN_OSPathLink *link = DN_Arena_New(arena, DN_OSPathLink, DN_ZeroMem_Yes);
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
link->string = delimiter.lhs;
|
||||
link->prev = fs_path->tail;
|
||||
if (fs_path->tail) {
|
||||
fs_path->tail->next = link;
|
||||
} else {
|
||||
fs_path->head = link;
|
||||
}
|
||||
fs_path->tail = link;
|
||||
fs_path->links_size += 1;
|
||||
fs_path->string_size += delimiter.lhs.size;
|
||||
}
|
||||
|
||||
if (!delimiter.lhs.data)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAdd(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 copy = DN_Str8_Copy(arena, path);
|
||||
bool result = DN_Str8_HasData(copy) ? true : DN_OS_PathAddRef(arena, fs_path, copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAddF(DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(arena, fmt, args);
|
||||
va_end(args);
|
||||
bool result = DN_OS_PathAddRef(arena, fs_path, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathPop(DN_OSPath *fs_path)
|
||||
{
|
||||
if (!fs_path)
|
||||
return false;
|
||||
|
||||
if (fs_path->tail) {
|
||||
DN_ASSERT(fs_path->head);
|
||||
fs_path->links_size -= 1;
|
||||
fs_path->string_size -= fs_path->tail->string.size;
|
||||
fs_path->tail = fs_path->tail->prev;
|
||||
if (fs_path->tail) {
|
||||
fs_path->tail->next = nullptr;
|
||||
} else {
|
||||
fs_path->head = nullptr;
|
||||
}
|
||||
} else {
|
||||
DN_ASSERT(!fs_path->head);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathTo(DN_Arena *arena, DN_Str8 path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_OSPath fs_path = {};
|
||||
DN_OS_PathAddRef(arena, &fs_path, path);
|
||||
DN_Str8 result = DN_OS_PathBuildWithSeparator(arena, &fs_path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathToF(DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_Path(DN_Arena *arena, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, DN_OSPathSeperatorString);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathF(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_Path(arena, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator(DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!fs_path || fs_path->links_size <= 0)
|
||||
return result;
|
||||
|
||||
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
|
||||
DN_USize string_size = (fs_path->has_prefix_path_separator ? path_separator.size : 0) + fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size);
|
||||
result = DN_Str8_Alloc(arena, string_size, DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
char *dest = result.data;
|
||||
if (fs_path->has_prefix_path_separator) {
|
||||
DN_MEMCPY(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
|
||||
for (DN_OSPathLink *link = fs_path->head; link; link = link->next) {
|
||||
DN_Str8 string = link->string;
|
||||
DN_MEMCPY(dest, string.data, string.size);
|
||||
dest += string.size;
|
||||
|
||||
if (link != fs_path->tail) {
|
||||
DN_MEMCPY(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.data[string_size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: [$EXEC] DN_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSExecResult DN_OS_Exec(DN_Slice<DN_Str8> cmd_line,
|
||||
DN_OSExecArgs *args,
|
||||
DN_Arena *arena,
|
||||
DN_ErrSink *error)
|
||||
{
|
||||
DN_OSExecAsyncHandle async_handle = DN_OS_ExecAsync(cmd_line, args, error);
|
||||
DN_OSExecResult result = DN_OS_ExecWait(async_handle, arena, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena)
|
||||
{
|
||||
DN_ErrSink *error = DN_ErrSink_Begin(DN_ErrSinkMode_Nil);
|
||||
DN_OSExecResult result = DN_OS_Exec(cmd_line, args, arena, error);
|
||||
if (result.os_error_code) {
|
||||
DN_ErrSink_EndAndExitIfErrorF(
|
||||
error,
|
||||
result.os_error_code,
|
||||
"OS failed to execute the requested command returning the error code %u",
|
||||
result.os_error_code);
|
||||
}
|
||||
|
||||
if (result.exit_code) {
|
||||
DN_ErrSink_EndAndExitIfErrorF(
|
||||
error,
|
||||
result.exit_code,
|
||||
"OS executed command and returned non-zero exit code %u",
|
||||
result.exit_code);
|
||||
}
|
||||
|
||||
DN_ErrSink_EndAndIgnore(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$THRD] DN_OSThread //////////////////////////////////////////////////////////////////////
|
||||
DN_THREAD_LOCAL DN_TLS *g_dn_os_thread_tls;
|
||||
|
||||
static void DN_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
DN_OSThread *thread = DN_CAST(DN_OSThread *)user_context;
|
||||
DN_TLS_Init(&thread->tls);
|
||||
DN_OS_ThreadSetTLS(&thread->tls);
|
||||
DN_OS_SemaphoreWait(&thread->init_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ThreadSetTLS(DN_TLS *tls)
|
||||
{
|
||||
g_dn_os_thread_tls = tls;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name)
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
tls->name_size = DN_CAST(uint8_t)DN_MIN(name.size, sizeof(tls->name) - 1);
|
||||
DN_MEMCPY(tls->name, name.data, tls->name_size);
|
||||
tls->name[tls->name_size] = 0;
|
||||
|
||||
#if defined(DN_OS_WIN32)
|
||||
DN_Win_ThreadSetName(name);
|
||||
#else
|
||||
DN_Posix_ThreadSetName(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: [$HTTP] DN_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
|
||||
{
|
||||
if (response && DN_OS_SemaphoreIsValid(&response->on_complete_semaphore))
|
||||
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers)
|
||||
{
|
||||
// TODO(doyle): Revise the memory allocation and its lifetime
|
||||
DN_OSHttpResponse result = {};
|
||||
DN_TLSTMem tmem = DN_TLS_TMem(arena);
|
||||
result.tmem_arena = tmem.arena;
|
||||
|
||||
DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
|
||||
DN_OS_HttpRequestWait(&result);
|
||||
return result;
|
||||
}
|
445
dqn_os.h
445
dqn_os.h
@ -1,445 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__|
|
||||
// $$ | $$ |\$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ |$$\ $$ |
|
||||
// $$$$$$ |\$$$$$$ |
|
||||
// \______/ \______/
|
||||
//
|
||||
// dqn_os.h -- Common APIs/services provided by the operating system/platform layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$OMEM] DN_OSMem -- -- Memory allocation (typically virtual memory if supported)
|
||||
// [$DATE] DN_OSDate -- -- Date time APIs
|
||||
// [$FILE] DN_OSPathInfo/File -- -- File path info/reading/writing
|
||||
// [$PATH] DN_OSPath -- -- Construct native OS paths helpers
|
||||
// [$EXEC] DN_OSExec -- -- Execute programs programatically
|
||||
// [$SEMA] DN_OSSemaphore -- DN_SEMAPHORE --
|
||||
// [$MUTX] DN_OSMutex -- --
|
||||
// [$THRD] DN_OSThread -- DN_THREAD --
|
||||
// [$HTTP] DN_OSHttp -- --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$OMEM] DN_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSMemCommit
|
||||
{
|
||||
DN_OSMemCommit_No,
|
||||
DN_OSMemCommit_Yes,
|
||||
};
|
||||
|
||||
enum DN_OSMemPage
|
||||
{
|
||||
// Exception on read/write with a page. This flag overrides the read/write
|
||||
// access.
|
||||
DN_OSMemPage_NoAccess = 1 << 0,
|
||||
|
||||
DN_OSMemPage_Read = 1 << 1, // Only read permitted on the page.
|
||||
|
||||
// Only write permitted on the page. On Windows this is not supported and
|
||||
// will be promoted to read+write permissions.
|
||||
DN_OSMemPage_Write = 1 << 2,
|
||||
|
||||
DN_OSMemPage_ReadWrite = DN_OSMemPage_Read | DN_OSMemPage_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 DN_OSMemPage_NoAccess.
|
||||
// This flag must only be used in DN_OSMem_Protect
|
||||
DN_OSMemPage_Guard = 1 << 3,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
DN_OSMemPage_AllocRecordLeakPermitted = 1 << 4,
|
||||
|
||||
// If leak tracing is enabled this flag will prevent any allocation record
|
||||
// from being created in the allocation table at all. If this flag is
|
||||
// enabled, 'OSMemPage_AllocRecordLeakPermitted' has no effect since the
|
||||
// record will never be created.
|
||||
DN_OSMemPage_NoAllocRecordEntry = 1 << 5,
|
||||
|
||||
// [INTERNAL] Do not use. All flags together do not constitute a correct
|
||||
// configuration of pages.
|
||||
DN_OSMemPage_All = DN_OSMemPage_NoAccess |
|
||||
DN_OSMemPage_ReadWrite |
|
||||
DN_OSMemPage_Guard |
|
||||
DN_OSMemPage_AllocRecordLeakPermitted |
|
||||
DN_OSMemPage_NoAllocRecordEntry,
|
||||
};
|
||||
|
||||
// NOTE: [$DATE] DN_OSDate ////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSDateTimeStr8
|
||||
{
|
||||
char date[DN_ARRAY_UCOUNT("YYYY-MM-SS")];
|
||||
uint8_t date_size;
|
||||
char hms[DN_ARRAY_UCOUNT("HH:MM:SS")];
|
||||
uint8_t hms_size;
|
||||
};
|
||||
|
||||
struct DN_OSDateTime
|
||||
{
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
uint8_t hour;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
};
|
||||
|
||||
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: [$FSYS] DN_OSFile ////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSPathInfoType
|
||||
{
|
||||
DN_OSPathInfoType_Unknown,
|
||||
DN_OSPathInfoType_Directory,
|
||||
DN_OSPathInfoType_File,
|
||||
};
|
||||
|
||||
struct DN_OSPathInfo
|
||||
{
|
||||
bool exists;
|
||||
DN_OSPathInfoType type;
|
||||
uint64_t create_time_in_s;
|
||||
uint64_t last_write_time_in_s;
|
||||
uint64_t last_access_time_in_s;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct DN_OSDirIterator
|
||||
{
|
||||
void *handle;
|
||||
DN_Str8 file_name;
|
||||
char buffer[512];
|
||||
};
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSFile
|
||||
{
|
||||
bool error;
|
||||
void *handle;
|
||||
};
|
||||
|
||||
enum DN_OSFileOpen
|
||||
{
|
||||
DN_OSFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
|
||||
DN_OSFileOpen_OpenIfExist, // Open file at path only if it exists
|
||||
DN_OSFileOpen_OpenAlways, // Open file at path, create file if it does not exist
|
||||
};
|
||||
|
||||
typedef uint32_t DN_OSFileAccess;
|
||||
enum DN_OSFileAccess_
|
||||
{
|
||||
DN_OSFileAccess_Read = 1 << 0,
|
||||
DN_OSFileAccess_Write = 1 << 1,
|
||||
DN_OSFileAccess_Execute = 1 << 2,
|
||||
DN_OSFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
|
||||
DN_OSFileAccess_ReadWrite = DN_OSFileAccess_Read | DN_OSFileAccess_Write,
|
||||
DN_OSFileAccess_All = DN_OSFileAccess_ReadWrite | DN_OSFileAccess_Execute | DN_OSFileAccess_AppendOnly,
|
||||
};
|
||||
#endif // DN_NO_OS_FILE_API
|
||||
|
||||
// NOTE: DN_OSPath ////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_OSPathSeperator)
|
||||
#if defined(DN_OS_WIN32)
|
||||
#define DN_OSPathSeperator "\\"
|
||||
#else
|
||||
#define DN_OSPathSeperator "/"
|
||||
#endif
|
||||
#define DN_OSPathSeperatorString DN_STR8(DN_OSPathSeperator)
|
||||
#endif
|
||||
|
||||
struct DN_OSPathLink
|
||||
{
|
||||
DN_Str8 string;
|
||||
DN_OSPathLink *next;
|
||||
DN_OSPathLink *prev;
|
||||
};
|
||||
|
||||
struct DN_OSPath
|
||||
{
|
||||
bool has_prefix_path_separator;
|
||||
DN_OSPathLink *head;
|
||||
DN_OSPathLink *tail;
|
||||
DN_USize string_size;
|
||||
uint16_t links_size;
|
||||
};
|
||||
|
||||
// NOTE: [$EXEC] DN_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
typedef uint32_t DN_OSExecFlags;
|
||||
enum DN_OSExecFlags_
|
||||
{
|
||||
DN_OSExecFlags_Nil = 0,
|
||||
DN_OSExecFlags_SaveStdout = 1 << 0,
|
||||
DN_OSExecFlags_SaveStderr = 1 << 1,
|
||||
DN_OSExecFlags_SaveOutput = DN_OSExecFlags_SaveStdout | DN_OSExecFlags_SaveStderr,
|
||||
DN_OSExecFlags_MergeStderrToStdout = 1 << 2 | DN_OSExecFlags_SaveOutput,
|
||||
};
|
||||
|
||||
struct DN_OSExecAsyncHandle
|
||||
{
|
||||
DN_OSExecFlags exec_flags;
|
||||
uint32_t os_error_code;
|
||||
uint32_t exit_code;
|
||||
void *process;
|
||||
void *stdout_read;
|
||||
void *stdout_write;
|
||||
void *stderr_read;
|
||||
void *stderr_write;
|
||||
};
|
||||
|
||||
struct DN_OSExecResult
|
||||
{
|
||||
bool finished;
|
||||
DN_Str8 stdout_text;
|
||||
DN_Str8 stderr_text;
|
||||
uint32_t os_error_code;
|
||||
uint32_t exit_code;
|
||||
};
|
||||
|
||||
struct DN_OSExecArgs
|
||||
{
|
||||
DN_OSExecFlags flags;
|
||||
DN_Str8 working_dir;
|
||||
DN_Slice<DN_Str8> environment;
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_SEMAPHORE)
|
||||
// NOTE: [$SEMA] DN_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
uint32_t const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
|
||||
|
||||
struct DN_OSSemaphore
|
||||
{
|
||||
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
void *win32_handle;
|
||||
#else
|
||||
sem_t posix_handle;
|
||||
bool posix_init;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DN_OSSemaphoreWaitResult
|
||||
{
|
||||
DN_OSSemaphoreWaitResult_Failed,
|
||||
DN_OSSemaphoreWaitResult_Success,
|
||||
DN_OSSemaphoreWaitResult_Timeout,
|
||||
};
|
||||
#endif // !defined(DN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$THRD] DN_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
|
||||
typedef int32_t (DN_OSThreadFunc)(struct DN_OSThread*);
|
||||
|
||||
struct DN_OSThread
|
||||
{
|
||||
DN_FStr8<64> name;
|
||||
DN_TLS tls;
|
||||
void *handle;
|
||||
uint64_t thread_id;
|
||||
void *user_context;
|
||||
DN_OSThreadFunc *func;
|
||||
DN_OSSemaphore init_semaphore;
|
||||
};
|
||||
#endif // !defined(DN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] DN_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSHttpRequestSecure
|
||||
{
|
||||
DN_OSHttpRequestSecure_No,
|
||||
DN_OSHttpRequestSecure_Yes,
|
||||
};
|
||||
|
||||
struct DN_OSHttpResponse
|
||||
{
|
||||
// NOTE: Response data
|
||||
uint32_t error_code;
|
||||
DN_Str8 error_msg;
|
||||
uint16_t http_status;
|
||||
DN_Str8 body;
|
||||
DN_B32 done;
|
||||
|
||||
// NOTE: Book-keeping
|
||||
DN_Arena *arena; // Allocates memory for the response
|
||||
|
||||
// NOTE: Async book-keeping
|
||||
// Synchronous HTTP response uses the TLS scratch arena whereas async
|
||||
// calls use their own dedicated arena.
|
||||
DN_Arena tmp_arena;
|
||||
DN_Arena *tmem_arena;
|
||||
DN_Str8Builder builder;
|
||||
DN_OSSemaphore on_complete_semaphore;
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_t *em_handle;
|
||||
#elif defined(DN_OS_WIN32)
|
||||
HINTERNET win32_request_session;
|
||||
HINTERNET win32_request_connection;
|
||||
HINTERNET win32_request_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
DN_API void DN_OS_Init();
|
||||
|
||||
// NOTE: [$OMEM] Memory //////////////////////////////////////////////////////////////////////////
|
||||
DN_API void * DN_OS_MemReserve (DN_USize size, DN_OSMemCommit commit, uint32_t page_flags);
|
||||
DN_API bool DN_OS_MemCommit (void *ptr, DN_USize size, uint32_t page_flags);
|
||||
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size);
|
||||
DN_API void DN_OS_MemRelease (void *ptr, DN_USize size);
|
||||
DN_API int DN_OS_MemProtect (void *ptr, DN_USize size, uint32_t page_flags);
|
||||
|
||||
// NOTE: Heap
|
||||
DN_API void *DN_OS_MemAlloc (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API void DN_OS_MemDealloc (void *ptr);
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTime DN_OS_DateLocalTimeNow ();
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8 (DN_OSDateTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DN_API uint64_t DN_OS_DateUnixTimeNs ();
|
||||
DN_API uint64_t DN_OS_DateUnixTimeS ();
|
||||
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate (uint64_t time);
|
||||
DN_API uint64_t DN_OS_DateLocalToUnixTimeS(DN_OSDateTime date);
|
||||
DN_API uint64_t DN_OS_DateToUnixTimeS (DN_OSDateTime date);
|
||||
DN_API bool DN_OS_DateIsValid (DN_OSDateTime date);
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value);
|
||||
DN_API DN_Str8 DN_OS_EXEPath (DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_OS_EXEDir (DN_Arena *arena);
|
||||
#define DN_OS_EXEDir_TLS() DN_OS_EXEDir(DN_TLS_TopArena())
|
||||
DN_API void DN_OS_SleepMs (DN_UInt milliseconds);
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API uint64_t DN_OS_PerfCounterNow ();
|
||||
DN_API uint64_t DN_OS_PerfCounterFrequency();
|
||||
DN_API DN_F64 DN_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs (uint64_t begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin ();
|
||||
DN_API void DN_OS_TimerEnd (DN_OSTimer *timer);
|
||||
DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer);
|
||||
DN_API uint64_t DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSPathInfo DN_OS_PathInfo (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against);
|
||||
DN_API bool DN_OS_PathDelete (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_CopyFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_MoveFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_MakeDir (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirIterate (DN_Str8 path, DN_OSDirIterator *it);
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSFile DN_OS_FileOpen (DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_FileRead (DN_OSFile *file, void *buffer, DN_USize size, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *data, DN_USize size, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_FileWrite (DN_OSFile *file, DN_Str8 buffer, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_FileWriteFV (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_FileWriteF (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_FileFlush (DN_OSFile *file, DN_ErrSink *err);
|
||||
DN_API void DN_OS_FileClose (DN_OSFile *file);
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll (DN_Arena *arena, DN_Str8 path, DN_ErrSink *err);
|
||||
#define DN_OS_ReadAll_TLS(...) DN_OS_ReadAll(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_WriteAll (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllFV (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllF (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_WriteAllSafe (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllSafeFV (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllSafeF (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAddRef_TLS(...) DN_OS_PathAddRef(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddRef_Frame(...) DN_OS_PathAddRef(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAdd (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAdd_TLS(...) DN_OS_PathAdd(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAdd_Frame(...) DN_OS_PathAdd(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAddF (DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathAddF_TLS(...) DN_OS_PathAddF(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddF_Frame(...) DN_OS_PathAddF(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathPop (DN_OSPath *fs_path);
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator (DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator);
|
||||
#define DN_OS_PathBuildWithSeperator_TLS(...) DN_OS_PathBuildWithSeperator(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathBuildWithSeperator_Frame(...) DN_OS_PathBuildWithSeperator(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathTo (DN_Arena *arena, DN_Str8 path, DN_Str8 path_separtor);
|
||||
#define DN_OS_PathTo_TLS(...) DN_OS_PathTo(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathTo_Frame(...) DN_OS_PathTo(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathToF (DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathToF_TLS(...) DN_OS_PathToF(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathToF_Frame(...) DN_OS_PathToF(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_Path (DN_Arena *arena, DN_Str8 path);
|
||||
#define DN_OS_Path_TLS(...) DN_OS_Path(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_Path_Frame(...) DN_OS_Path(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathF_TLS(...) DN_OS_PathF(DN_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathF_Frame(...) DN_OS_PathF(DN_TLS_FrameArena(), ##__VA_ARGS__)
|
||||
|
||||
#define DN_OS_PathBuildFwdSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("/"))
|
||||
#define DN_OS_PathBuildBackSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("\\"))
|
||||
#define DN_OS_PathBuild(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_OSPathSeparatorString)
|
||||
|
||||
// NOTE: [$EXEC] DN_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_Exit (int32_t exit_code);
|
||||
DN_API DN_OSExecResult DN_OS_ExecPump (DN_OSExecAsyncHandle handle, char *stdout_buffer, size_t *stdout_size, char *stderr_buffer, size_t *stderr_size, uint32_t timeout_ms, DN_ErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecWait (DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_ErrSink *err);
|
||||
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_ErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_Exec (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena, DN_ErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena);
|
||||
#define DN_OS_ExecOrAbort_TLS(...) DN_OS_ExecOrAbort(__VA_ARGS__, DN_TLS_TopArena())
|
||||
|
||||
// NOTE: [$SEMA] DN_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_SEMAPHORE)
|
||||
DN_API DN_OSSemaphore DN_OS_SemaphoreInit (uint32_t initial_count);
|
||||
DN_API bool DN_OS_SemaphoreIsValid (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreDeinit (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, uint32_t amount);
|
||||
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait (DN_OSSemaphore *semaphore, uint32_t timeout_ms);
|
||||
#endif // !defined(DN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$MUTX] DN_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSMutex DN_OS_MutexInit ();
|
||||
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexLock (DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex);
|
||||
#define DN_OS_Mutex(mutex) DN_DEFER_LOOP(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
|
||||
|
||||
// NOTE: [$THRD] DN_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
|
||||
DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context);
|
||||
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread);
|
||||
DN_API uint32_t DN_OS_ThreadID ();
|
||||
DN_API void DN_OS_ThreadSetTLS(DN_TLS *tls);
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name);
|
||||
#endif // !defined(DN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] DN_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
||||
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
|
||||
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
1336
dqn_os_posix.cpp
1336
dqn_os_posix.cpp
File diff suppressed because it is too large
Load Diff
1728
dqn_os_win32.cpp
1728
dqn_os_win32.cpp
File diff suppressed because it is too large
Load Diff
1291
dqn_string.cpp
1291
dqn_string.cpp
File diff suppressed because it is too large
Load Diff
150
dqn_tls.cpp
150
dqn_tls.cpp
@ -1,150 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$$$$$\
|
||||
// \__$$ __|$$ | $$ __$$\
|
||||
// $$ | $$ | $$ / \__|
|
||||
// $$ | $$ | \$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ | $$\ $$ |
|
||||
// $$ | $$$$$$$$\\$$$$$$ |
|
||||
// \__| \________|\______/
|
||||
//
|
||||
// dqn_tls.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$TCTX] DN_TLS /////////////////////////////////////////////////////////////////
|
||||
DN_TLSTMem::DN_TLSTMem(DN_TLS *tls, uint8_t arena_index, DN_TLSPushTMem push_tmem)
|
||||
{
|
||||
DN_ASSERT(arena_index == DN_TLSArena_TMem0 || arena_index == DN_TLSArena_TMem1);
|
||||
arena = tls->arenas + arena_index;
|
||||
temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
push_arena = push_tmem;
|
||||
if (push_arena)
|
||||
DN_TLS_PushArena(arena);
|
||||
}
|
||||
|
||||
DN_TLSTMem::~DN_TLSTMem()
|
||||
{
|
||||
DN_ASSERT(destructed == false);
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
if (push_arena)
|
||||
DN_TLS_PopArena();
|
||||
}
|
||||
|
||||
DN_API void DN_TLS_Init(DN_TLS *tls)
|
||||
{
|
||||
DN_CHECK(tls);
|
||||
if (tls->init)
|
||||
return;
|
||||
|
||||
DN_FOR_UINDEX (index, DN_TLSArena_Count) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
switch (DN_CAST(DN_TLSArena)index) {
|
||||
default: *arena = DN_Arena_InitSize(DN_MEGABYTES(4), DN_KILOBYTES(4), DN_ArenaFlags_AllocCanLeak); break;
|
||||
case DN_TLSArena_ErrorSink: *arena = DN_Arena_InitSize(DN_KILOBYTES(64), DN_KILOBYTES(4), DN_ArenaFlags_AllocCanLeak); break;
|
||||
case DN_TLSArena_Count: DN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
}
|
||||
|
||||
tls->thread_id = DN_OS_ThreadID();
|
||||
tls->err_sink.arena = tls->arenas + DN_TLSArena_ErrorSink;
|
||||
tls->init = true;
|
||||
}
|
||||
|
||||
DN_API void DN_TLS_Deinit(DN_TLS *tls)
|
||||
{
|
||||
tls->init = false;
|
||||
tls->err_sink = {};
|
||||
tls->arena_stack_index = {};
|
||||
DN_FOR_UINDEX(index, DN_TLSArena_Count)
|
||||
{
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
DN_Arena_Deinit(arena);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_TLS *DN_TLS_Get()
|
||||
{
|
||||
DN_TLS *result = g_dn_os_thread_tls;
|
||||
DN_ASSERT(
|
||||
g_dn_core->init &&
|
||||
"Library context must be be initialised first by calling DN_Library_Init. This "
|
||||
"initialises the main thread's TLS for you (no need to call DN_OS_ThreadSetTLS on main)");
|
||||
|
||||
DN_ASSERT(result &&
|
||||
"Thread must be assigned the TLS with DN_OS_ThreadSetTLS. If the library is "
|
||||
"initialised, then, this thread was created without calling the set TLS function "
|
||||
"for the spawned thread.");
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_TLS_Arena()
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_Arena *result = tls->arenas + DN_TLSArena_Main;
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DN_API DN_TLSTMem DN_TLS_GetTMem(void const *conflict_arena, DN_TLSPushTMem push_tmem)
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
uint8_t tls_index = (uint8_t)-1;
|
||||
for (uint8_t index = DN_TLSArena_TMem0; index <= DN_TLSArena_TMem1; index++) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
tls_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DN_ASSERT(tls_index != (uint8_t)-1);
|
||||
return DN_TLSTMem(tls, tls_index, push_tmem);
|
||||
}
|
||||
|
||||
DN_API void DN_TLS_PushArena(DN_Arena *arena)
|
||||
{
|
||||
DN_ASSERT(arena);
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_ASSERT(tls->arena_stack_index < DN_ARRAY_UCOUNT(tls->arena_stack));
|
||||
tls->arena_stack[tls->arena_stack_index++] = arena;
|
||||
}
|
||||
|
||||
DN_API void DN_TLS_PopArena()
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_ASSERT(tls->arena_stack_index > 0);
|
||||
tls->arena_stack_index--;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_TLS_TopArena()
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_Arena *result = nullptr;
|
||||
if (tls->arena_stack_index)
|
||||
result = tls->arena_stack[tls->arena_stack_index - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_TLS_BeginFrame(DN_Arena *frame_arena)
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
tls->frame_arena = frame_arena;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_TLS_FrameArena()
|
||||
{
|
||||
DN_TLS *tls = DN_TLS_Get();
|
||||
DN_Arena *result = tls->frame_arena;
|
||||
return result;
|
||||
}
|
||||
|
93
dqn_tls.h
93
dqn_tls.h
@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
enum DN_TLSArena
|
||||
{
|
||||
DN_TLSArena_Main, // NOTE: Arena for Permanent allocations
|
||||
DN_TLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
|
||||
|
||||
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
|
||||
DN_TLSArena_TMem0,
|
||||
DN_TLSArena_TMem1,
|
||||
|
||||
DN_TLSArena_Count,
|
||||
};
|
||||
|
||||
struct DN_TLS
|
||||
{
|
||||
DN_B32 init; // Flag to track if Thread has been initialised
|
||||
uint64_t thread_id;
|
||||
DN_CallSite call_site; // Stores call-site information when requested by thread
|
||||
DN_ErrSink err_sink; // Error handling state
|
||||
DN_Arena arenas[DN_TLSArena_Count];
|
||||
|
||||
// Push and pop arenas onto the stack. Functions suffixed 'TLS' will use
|
||||
// these arenas for memory allocation.
|
||||
DN_Arena *arena_stack[8];
|
||||
DN_USize arena_stack_index;
|
||||
|
||||
DN_Arena *frame_arena;
|
||||
char name[64];
|
||||
uint8_t name_size;
|
||||
};
|
||||
|
||||
// Push the temporary memory arena when retrieved, popped when the arena goes
|
||||
// out of scope. Pushed arenas are used automatically as the allocator in TLS
|
||||
// suffixed function.
|
||||
enum DN_TLSPushTMem
|
||||
{
|
||||
DN_TLSPushTMem_No,
|
||||
DN_TLSPushTMem_Yes,
|
||||
};
|
||||
|
||||
struct DN_TLSTMem
|
||||
{
|
||||
DN_TLSTMem(DN_TLS *context, uint8_t context_index, DN_TLSPushTMem push_scratch);
|
||||
~DN_TLSTMem();
|
||||
|
||||
DN_Arena *arena;
|
||||
DN_B32 destructed;
|
||||
DN_TLSPushTMem push_arena;
|
||||
DN_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
DN_API void DN_TLS_Init(DN_TLS *tls);
|
||||
DN_API void DN_TLS_Deinit(DN_TLS *tls);
|
||||
DN_API DN_TLS * DN_TLS_Get();
|
||||
DN_API DN_Arena * DN_TLS_Arena();
|
||||
#define DN_TLS_SaveCallSite do { DN_TLS_Get()->call_site = DN_CALL_SITE; } while (0)
|
||||
DN_API DN_TLSTMem DN_TLS_GetTMem(void const *conflict_arena, DN_TLSPushTMem push_tmp_mem);
|
||||
#define DN_TLS_TMem(...) DN_TLS_GetTMem(__VA_ARGS__, DN_TLSPushTMem_No)
|
||||
#define DN_TLS_PushTMem(...) DN_TLS_GetTMem(__VA_ARGS__, DN_TLSPushTMem_Yes)
|
||||
DN_API void DN_TLS_PushArena(DN_Arena *arena);
|
||||
DN_API void DN_TLS_PopArena();
|
||||
DN_API DN_Arena * DN_TLS_TopArena();
|
||||
|
||||
DN_API void DN_TLS_BeginFrame(DN_Arena *frame_arena);
|
||||
DN_API DN_Arena * DN_TLS_FrameArena();
|
2206
dqn_unit_tests.cpp
2206
dqn_unit_tests.cpp
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
||||
#include "dqn.h"
|
||||
#include "dqn.cpp"
|
||||
#include "dqn_unit_tests.cpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argv; (void)argc;
|
||||
DN_Core *core = (DN_Core *)DN_OS_MemAlloc(sizeof(DN_Core), DN_ZeroMem_Yes);
|
||||
DN_Core_Init(core, DN_CoreOnInit_LogAllFeatures);
|
||||
DN_Test_RunSuite();
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user