2025 spring cleaning

This commit is contained in:
doylet 2025-05-12 17:09:03 +10:00
parent 70d0889b00
commit 2371297dda
102 changed files with 23632 additions and 22870 deletions

743
Base/dn_base.cpp Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

277
Base/dn_base_containers.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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

File diff suppressed because it is too large Load Diff

200
Extra/dn_cgen.h Normal file
View 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
View 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
View 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)

View File

@ -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)

View File

@ -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
View 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
View 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)

View File

@ -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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

31
Extra/dn_tests_main.cpp Normal file
View 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

View File

@ -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
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

271
SIMD/dn_simd_avx512f.cpp Normal file
View 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;
}

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 (

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -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
View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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();

File diff suppressed because it is too large Load Diff

View File

@ -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